| Server IP : 13.126.101.145 / Your IP : 216.73.217.50 Web Server : Apache/2.4.52 (Ubuntu) System : Linux ip-11-115-0-196 6.8.0-1039-aws #41~22.04.1-Ubuntu SMP Thu Sep 11 10:54:48 UTC 2025 x86_64 User : www-data ( 33) PHP Version : 8.3.17 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/html/rentals_updated/wp-content/plugins/wpo365-login/Services/ |
Upload File : |
<?php
namespace Wpo\Services;
use WP_Error;
use \Wpo\Core\Domain_Helpers;
use \Wpo\Core\Url_Helpers;
use \Wpo\Core\WordPress_Helpers;
use \Wpo\Core\Wpmu_Helpers;
use \Wpo\Services\Error_Service;
use \Wpo\Services\Log_Service;
use \Wpo\Services\Options_Service;
use \Wpo\Services\Request_Service;
use \Wpo\Services\Saml2_Service;
use \Wpo\Services\User_Service;
// Prevent public access to this script
defined('ABSPATH') or die();
if (!class_exists('\Wpo\Services\Authentication_Service')) {
class Authentication_Service
{
const USR_META_WPO365_AUTH = 'WPO365_AUTH';
/**
* @param $force boolean If true the test whether authentication can be skipped will be skipped.
*/
public static function authenticate_request($force = false)
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
$request_service = Request_Service::get_instance();
$request = $request_service->get_request($GLOBALS['WPO_CONFIG']['request_id']);
/**
* @since 23.0 If the audience check fails and the administrator has configured the plugin
* not to exit when the audience check fails.
*/
if (true === $request->get_item('skip_authentication')) {
return;
}
$wp_usr_id = get_current_user_id();
$wpo_auth_value = get_user_meta(
$wp_usr_id,
self::USR_META_WPO365_AUTH,
true
);
$request->set_item('wpo_auth_value', $wpo_auth_value);
$request->set_item('wp_usr_id', $wp_usr_id);
if (!$force && self::skip_authentication()) {
return;
}
// Logged-on WP-only user
if (is_user_logged_in() && empty($wpo_auth_value)) {
Log_Service::write_log('DEBUG', __METHOD__ . ' -> User is a Wordpress-only user so no authentication is required');
return;
}
// User not logged on
if (empty($wpo_auth_value)) {
// If multiple IdPs have been configured then the user must first select one
if (!empty(Wp_Config_Service::get_multiple_idps())) {
Log_Service::write_log('DEBUG', sprintf('%s -> Multiple IdPs have been configured and the user has not selected one and therefore he / she is redirected to the login page instead', __METHOD__));
Authentication_Service::goodbye(Error_Service::NO_IDP_SELECTED);
exit();
}
Log_Service::write_log('DEBUG', __METHOD__ . ' -> User is not logged in and therefore sending the user to Microsoft to sign in');
$login_hint = isset($_REQUEST['login_hint'])
? \sanitize_text_field($_REQUEST['login_hint'])
: null;
self::redirect_to_microsoft($login_hint);
}
// Check if user has expired
$wpo_auth = json_decode($wpo_auth_value);
// If 0 then session expiration check is skipped
if (Options_Service::get_global_numeric_var('session_duration') > 0) {
$auth_expired = !isset($wpo_auth->expiry) || $wpo_auth->expiry < time();
if ($auth_expired) {
$upn = User_Service::try_get_user_principal_name($wp_usr_id);
$login_hint = !empty($upn) ? $upn : null;
do_action('destroy_wpo365_session');
// Don't call wp_logout because it may be extended
wp_destroy_current_session();
wp_clear_auth_cookie();
wp_set_current_user(0);
unset($_COOKIE[AUTH_COOKIE]);
unset($_COOKIE[SECURE_AUTH_COOKIE]);
unset($_COOKIE[LOGGED_IN_COOKIE]);
Log_Service::write_log('DEBUG', __METHOD__ . ' -> User logged out because current login not valid anymore (' . $auth_expired . ')');
self::redirect_to_microsoft($login_hint);
}
} else {
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Session expiration ignored because the administrator configured a duration of 0');
}
$current_blog_id = \get_current_blog_id();
// WPMU Dedicated
if (is_multisite() && Options_Service::mu_use_subsite_options() && !\is_user_member_of_blog($wp_usr_id, $current_blog_id)) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> User with ID ' . $wp_usr_id . ' is not a member of the current site with ID ' . $current_blog_id . '.');
Authentication_Service::goodbye(Error_Service::USER_NOT_FOUND);
exit();
}
}
/**
* @since 11.0
*/
public static function authenticate_oidc_user()
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
$request_service = Request_Service::get_instance();
$request = $request_service->get_request($GLOBALS['WPO_CONFIG']['request_id']);
/**
* Switch the blog context if WPMU is detected and the user is trying to access
* a subsite but landed at the main site because of Microsoft redirecting the
* user there immediately after successful authentication.
*/
$state = $request->get_item('state');
$id_token = $request->get_item('id_token');
$auth_code = $request->get_item('code');
Wpmu_Helpers::switch_blog($state);
if (empty($id_token)) {
$error_message = sprintf('%s -> ID token could not be extracted from request storage.', __METHOD__);
Log_Service::write_log('ERROR', $error_message);
Authentication_Service::goodbye(Error_Service::ID_TOKEN_ERROR);
exit();
}
$wpo_usr = Options_Service::get_global_boolean_var('use_b2c') && \class_exists('\Wpo\Services\Id_Token_Service_B2c')
? User_Service::user_from_b2c_id_token($id_token)
: User_Service::user_from_id_token($id_token);
self::user_in_group($wpo_usr);
do_action(
'wpo365/oidc/authenticating',
$wpo_usr->preferred_username,
$wpo_usr->email,
$wpo_usr->groups
);
/**
* Authenticate but don't sign in Azure AD users.
*
* @since 16.0
*/
if (true === apply_filters('wpo365/cookie/set', $wpo_usr, $state)) {
return $wpo_usr;
}
$wp_usr = User_Service::ensure_user($wpo_usr);
if (empty($wp_usr)) {
$error_message = sprintf('%s -> Multiple errors occurred: please check debug log for previous errors', __METHOD__);
Log_Service::write_log('ERROR', $error_message);
Authentication_Service::goodbye(Error_Service::CHECK_LOG);
exit();
}
// Now log on the user
wp_set_auth_cookie($wp_usr->ID, Options_Service::get_global_boolean_var('remember_user')); // Both log user on
wp_set_current_user($wp_usr->ID); // And set current user
// Session valid until
$session_duration = Options_Service::get_global_numeric_var('session_duration');
$session_duration = empty($session_duration) ? 3480 : $session_duration;
$expiry = time() + intval($session_duration);
// Obfuscated user's wp id
$obfuscated_user_id = $expiry + $wp_usr->ID;
$wpo_auth = new \stdClass();
$wpo_auth->expiry = $expiry;
$wpo_auth->ouid = $obfuscated_user_id;
$wpo_auth->upn = $wpo_usr->upn;
$wpo_auth->url = $GLOBALS['WPO_CONFIG']['url_info']['wp_site_url'];
$wpo_auth->auth_code = md5($auth_code);
update_user_meta(
$wp_usr->ID,
self::USR_META_WPO365_AUTH,
json_encode($wpo_auth)
);
$request->set_item('wpo_auth_value', $wpo_auth);
/**
* Fires after the user has successfully logged in.
*
* @since 7.1
*
* @param string $user_login Username.
* @param WP_User $user WP_User object of the logged-in user.
*/
if (false === Options_Service::get_global_boolean_var('skip_wp_login_action')) {
do_action('wp_login', $wp_usr->user_login, $wp_usr);
}
/**
* @since 10.6
*
* The wpo365_openid_token_processed action hook signals to its subscribers
* that a user has just signed in successfully with Microsoft. As arguments
* it provides the WordPress user ID and the user's Azure AD group IDs
* as an one-dimensional array of GUIDs (as strings).
*/
do_action('wpo365_openid_token_processed', $wp_usr->ID, $wpo_usr->groups, $id_token);
/**
* @since 15.0
*/
do_action('wpo365/oidc/authenticated', $wp_usr->ID);
return $wpo_usr;
}
/**
*
*/
public static function authenticate_saml2_user()
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
$request_service = Request_Service::get_instance();
$request = $request_service->get_request($GLOBALS['WPO_CONFIG']['request_id']);
/**
* Switch the blog context if WPMU is detected and the user is trying to access
* a subsite but landed at the main site because of Microsoft redirecting the
* user there immediately after successful authentication.
*/
$state = $request->get_item('relay_state');
Wpmu_Helpers::switch_blog($state);
require_once($GLOBALS['WPO_CONFIG']['plugin_dir'] . '/OneLogin/_toolkit_loader.php');
$saml_settings = Saml2_Service::saml_settings();
$auth = new \OneLogin_Saml2_Auth($saml_settings);
$auth->processResponse();
// Check for errors
$errors = $auth->getErrors();
if (!empty($errors)) {
$error_reason = $auth->getLastErrorReason();
Log_Service::write_log('ERROR', __METHOD__ . ' -> Could not process SAML 2.0 response (See log for errors [' . $error_reason . '])');
Log_Service::write_log('WARN', $errors);
Authentication_Service::goodbye(Error_Service::SAML2_ERROR);
exit();
}
// Check if authentication was successful
if (!$auth->isAuthenticated()) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> User is not authenticated');
Authentication_Service::goodbye(Error_Service::SAML2_ERROR);
exit();
}
// Check against replay attack
Saml2_Service::check_message_id($auth->getLastMessageId());
// Abstraction to WPO365 User
$saml_attributes = $auth->getAttributes();
$saml_name_id = $auth->getNameId();
$wpo_usr = User_Service::user_from_saml_response($saml_name_id, $saml_attributes);
self::user_in_group($wpo_usr);
do_action(
'wpo365/saml2/authenticating',
$wpo_usr->preferred_username,
$wpo_usr->email,
$wpo_usr->groups
);
/**
* Authenticate but don't sign in Azure AD users.
*
* @since 16.0
*/
if (true === apply_filters('wpo365/cookie/set', $wpo_usr, $state)) {
return $wpo_usr;
}
$wp_usr = User_Service::ensure_user($wpo_usr);
if (empty($wp_usr)) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Multiple errors occurred: please check debug log for previous errors');
Authentication_Service::goodbye(Error_Service::CHECK_LOG);
exit();
}
// Now log on the user
wp_set_auth_cookie($wp_usr->ID, Options_Service::get_global_boolean_var('remember_user')); // Both log user on
wp_set_current_user($wp_usr->ID); // And set current user
// Session valid until
$session_duration = Options_Service::get_global_numeric_var('session_duration');
$session_duration = empty($session_duration) ? 3480 : $session_duration;
$expiry = time() + intval($session_duration);
// Obfuscated user's wp id
$obfuscated_user_id = $expiry + $wp_usr->ID;
$wpo_auth = new \stdClass();
$wpo_auth->expiry = $expiry;
$wpo_auth->ouid = $obfuscated_user_id;
$wpo_auth->upn = $wpo_usr->upn;
$wpo_auth->url = $GLOBALS['WPO_CONFIG']['url_info']['wp_site_url'];
update_user_meta(
$wp_usr->ID,
self::USR_META_WPO365_AUTH,
json_encode($wpo_auth)
);
$request->set_item('wpo_auth_value', $wpo_auth);
/**
* Fires after the user has successfully logged in.
*
* @since 7.1
*
* @param string $user_login Username.
* @param WP_User $user WP_User object of the logged-in user.
*/
if (false === Options_Service::get_global_boolean_var('skip_wp_login_action')) {
do_action('wp_login', $wp_usr->user_login, $wp_usr);
}
/**
* @since 15.0
*/
do_action('wpo365/saml/authenticated', $wp_usr->ID);
return $wpo_usr;
}
/**
* Redirects the user either back to site with an HTTP POST or when dual login
* is configured to the (custom) login form. The data POSTed tells the plugin
* to initiate the Sign in with Microsoft flow (both OpenID Connect + SAML).
*
* @since 8.0
*
* @param $login_hint string Login hint that will be added to the Open ID Connect link if present
*
* @return void
*/
public static function redirect_to_microsoft($login_hint = null)
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
if (!Options_Service::is_wpo365_configured()) {
Log_Service::write_log('WARN', sprintf('%s -> Attempt to initiate SSO failed because WPO365 is not configured', __METHOD__));
return;
}
if (class_exists('\Wpo\Services\Dual_Login_Service')) {
\Wpo\Services\Dual_Login_Service::redirect();
}
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Forwarding the user to Microsoft to get fresh ID and access token(s)');
/**
* @since 33.0 The loading template has become optional and instead the redirect to Microsoft
* is performed server-side.
*/
if (Options_Service::get_global_boolean_var('use_teams')) {
ob_start();
include($GLOBALS['WPO_CONFIG']['plugin_dir'] . '/templates/openid-redirect.php');
$content = ob_get_clean();
echo $content;
exit();
}
$redirect_url = Options_Service::get_aad_option('redirect_url');
$redirect_url = Options_Service::get_global_boolean_var('use_saml')
? Options_Service::get_aad_option('saml_sp_acs_url')
: $redirect_url;
$redirect_url = apply_filters('wpo365/aad/redirect_uri', $redirect_url);
$referer = (WordPress_Helpers::stripos($redirect_url, 'https') !== false ? 'https' : 'http') . '://' . $GLOBALS['WPO_CONFIG']['url_info']['host'] . $GLOBALS['WPO_CONFIG']['url_info']['request_uri'];
if (Options_Service::get_global_boolean_var('use_saml')) {
$params = array();
if (!empty($domain_hint = Options_Service::get_global_string_var('domain_hint'))) {
$params['whr'] = sanitize_text_field(\strtolower(\trim($domain_hint)));
}
\Wpo\Services\Saml2_Service::initiate_request($referer, $params);
exit();
} else {
if (Options_Service::get_global_boolean_var('use_b2c') && \class_exists('\Wpo\Services\Id_Token_Service_B2c')) {
$authUrl = \Wpo\Services\Id_Token_Service_B2c::get_openidconnect_url($login_hint, $referer);
} else if (Options_Service::get_global_boolean_var('use_ciam')) {
$authUrl = \Wpo\Services\Id_Token_Service_Ciam::get_openidconnect_url($login_hint, $referer);
} else {
$authUrl = Id_Token_Service::get_openidconnect_url($login_hint, $referer);
}
Url_Helpers::force_redirect($authUrl);
}
}
/**
* @since 11.0
*/
private static function user_in_group($wpo_usr)
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
// Check whether allowed (Office 365 or Security) Group Ids have been configured
$allowed_groups_ids = Options_Service::get_global_list_var('groups_whitelist');
if (sizeof($allowed_groups_ids) > 0) {
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Group policy has been defined');
if (empty($wpo_usr->groups) || !(count(
array_intersect_key(
array_flip($allowed_groups_ids),
$wpo_usr->groups
)
)) >= 1) {
$express_login = Options_Service::get_global_boolean_var('express_login');
if ($express_login) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Access denied error because the administrator has restricted
access to a limited number of Azure AD (security) groups but also enabled Express Login. As a result the plugin
can possibly not retrieve all Azure AD (security) groups that a user is a member of.');
} else {
Log_Service::write_log('WARN', __METHOD__ . ' -> Access denied error because the administrator has restricted
access to a limited number of Azure AD (security) groups and the user trying to log on
is not in one of these groups.');
}
self::goodbye(Error_Service::NOT_IN_GROUP);
exit();
}
}
}
/**
* @since 11.0
*/
public static function user_from_domain($preferred_username, $email)
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
// Check whether the user's domain is white listed (if empty this check is skipped)
$domains = Options_Service::get_global_list_var('domain_whitelist');
$domain_blocked_list = Options_Service::get_global_boolean_var('domain_blocked_list');
if (sizeof($domains) > 0) {
$login_domain = Domain_Helpers::get_smtp_domain_from_email_address($preferred_username);
$email_domain = Domain_Helpers::get_smtp_domain_from_email_address($email);
$match = function ($test_domain) use ($domains) {
foreach ($domains as $domain) {
if (empty($domain)) {
continue;
}
if (!empty($test_domain)) {
// Wildcard at the end -> stripos must be 0
if (substr($domain, -2) == '.*') {
$test_with = str_replace('.*', '', $domain);
if (WordPress_Helpers::stripos($test_domain, $test_with) === 0) {
return true;
}
}
// Wildcard at the beginning -> stripos must be greater than 0
else if (substr($domain, 0, 2) == '*.') {
$test_with = str_replace('*.', '', $domain);
if (WordPress_Helpers::stripos($test_domain, $test_with) >= 0) {
return true;
}
} else {
if (strcasecmp($test_domain, $domain) === 0) {
return true;
}
}
}
}
return false;
};
$found = $match($login_domain);
if (!$found) {
$found = $match($email_domain);
}
// Only users from specific domains are allowed to sign in
if (!$domain_blocked_list) {
if (!$found) {
Log_Service::write_log('WARN', sprintf(
'%s -> Access denied error because the administrator has restricted access to a limited number of domains [login: %s, email: %s]',
__METHOD__,
$login_domain,
$email_domain
));
self::goodbye(Error_Service::NOT_FROM_DOMAIN);
exit();
}
// Users from specific domains are NOT allowed to sign in
} else {
if ($found) {
Log_Service::write_log('WARN', sprintf(
'%s -> Access denied error because the administrator has blocked access for domain [login: %s, email: %s]',
__METHOD__,
$login_domain,
$email_domain
));
self::goodbye(Error_Service::NOT_FROM_DOMAIN);
exit();
}
}
}
}
/**
* Destroys any session and authenication artefacts and hooked up with wpo365_logout and should
* therefore never be called directly to avoid endless loops etc.
*
* @since 1.0
*
* @return void
*/
public static function destroy_session()
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
$wp_usr_id = get_current_user_id();
if (empty($wp_usr_id)) {
$request_service = Request_Service::get_instance();
$request = $request_service->get_request($GLOBALS['WPO_CONFIG']['request_id']);
$wp_usr_id = $request->get_item('wp_usr_id');
}
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Destroying session ' . strtolower(basename($_SERVER['PHP_SELF'])));
if (!empty($wp_usr_id)) {
delete_user_meta($wp_usr_id, self::USR_META_WPO365_AUTH);
delete_user_meta($wp_usr_id, Access_Token_Service::USR_META_WPO365_AUTH_CODE);
}
}
/**
* Same as destroy_session but with redirect to login page (but only if the
* login page isn't the current page).
*
* @since 1.0
*
* @param string $login_error_code Error code that is added to the logout url as query string parameter.
* @param bool $login_error Whether an error occurred during login (if not, then most likely during user creation)
* @return void
*/
public static function goodbye($login_error_code, $login_error = true)
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
$error_page_url = Options_Service::get_global_string_var('error_page_url');
$error_page_path = WordPress_Helpers::rtrim(parse_url($error_page_url, PHP_URL_PATH), '/');
$redirect_to = (empty($error_page_url) || $error_page_path === $GLOBALS['WPO_CONFIG']['url_info']['wp_site_path'])
? Url_Helpers::get_preferred_login_url()
: $error_page_url;
if (empty($_SERVER['PHP_SELF'])) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> $_SERVER[PHP_SELF] is empty. Please review your server configuration.');
}
do_action('destroy_wpo365_session');
if ($login_error) {
do_action('wpo365/user/loggedin/fail', $login_error_code);
} else {
do_action('wpo365/user/created/fail', $login_error_code);
}
wp_destroy_current_session();
wp_clear_auth_cookie();
wp_set_current_user(0);
unset($_COOKIE[AUTH_COOKIE]);
unset($_COOKIE[SECURE_AUTH_COOKIE]);
unset($_COOKIE[LOGGED_IN_COOKIE]);
$redirect_to = add_query_arg('login_errors', $login_error_code, $redirect_to);
Url_Helpers::force_redirect($redirect_to);
}
/**
* Helper hooked up to the wp_authenticate trigger to check if the user has been deactivated or not.
*
* @since 10.1
*
* @param $user_login string The user's login name
*
* @return void
*/
public static function is_deactivated($login = '', $kill_session = false)
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
$wp_usr = \get_user_by('login', $login);
if (!empty($wp_usr) && \get_user_meta($wp_usr->ID, 'wpo365_active', true) == 'deactivated') {
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Account ' . $wp_usr->login . ' is deactivated');
if ($kill_session) {
Authentication_Service::goodbye(Error_Service::DEACTIVATED);
exit();
}
$error_page_url = Options_Service::get_global_string_var('error_page_url');
$error_page_path = WordPress_Helpers::rtrim(parse_url($error_page_url, PHP_URL_PATH), '/');
$redirect_to = (empty($error_page_url) || $error_page_path === $GLOBALS['WPO_CONFIG']['url_info']['wp_site_path'])
? Url_Helpers::get_preferred_login_url()
: $error_page_url;
$redirect_to = add_query_arg('login_errors', Error_Service::DEACTIVATED, $redirect_to);
Url_Helpers::force_redirect($redirect_to);
}
}
/**
* Checks the configured scenario and the pages black list settings to
* decide whether or not authentication of the current page is needed.
*
* @since 5.0
*
* @return boolean True if validation should be skipped, otherwise false.
*/
private static function skip_authentication()
{
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
/**
* @since 31.0 Skip authentication for specific IP addresses
*/
if (!empty($ip_addresses = Options_Service::get_global_list_var('skip_ips'))) {
$remote_address = $_SERVER['REMOTE_ADDR'];
if (false !== filter_var($remote_address, FILTER_VALIDATE_IP)) {
foreach ($ip_addresses as $ip_address) {
if (false !== filter_var($ip_address, FILTER_VALIDATE_IP) && strcasecmp($ip_address, $remote_address) === 0) {
Log_Service::write_log('DEBUG', sprintf(
'%s -> Skipping authentication because the user\'s IP address %s is in the list of IP addresses freed from authentication',
__METHOD__,
$remote_address
));
return true;
}
}
}
}
/**
* @since 21.9 Skip authentication when wp-cli is detected.
*/
if (defined('WP_CLI') && constant('WP_CLI') === true && Options_Service::get_global_boolean_var('use_wp_cli')) {
Log_Service::write_log('DEBUG', sprintf('%s -> Skipping authentication [reason: wp-cli]', __METHOD__));
return true;
}
// Skip when a basic authentication header is detected
if (
true === Options_Service::get_global_boolean_var('skip_api_basic_auth_request')
&& Url_Helpers::is_basic_auth_api_request()
) {
return true;
}
// Not logged on and not configured => log in as WP Admin first
if (!is_user_logged_in() && (false === Options_Service::is_wpo365_configured())) {
return true;
}
// Bail out if the administrator configured to disable SSO for WP Admin
if ((is_admin() || is_network_admin()) && Options_Service::get_global_boolean_var('skip_wp_admin')) {
return true;
}
/**
* @since 16.0 If this is login and an wpo365 auth cookie is found then try
* to trick any page caching mechanism.
*/
do_action('wpo365/cookie/redirect');
/**
* @since 12.x
*
* Administrator enabled SSO for the login page and dual login is not enabled.
*/
if (true === Options_Service::get_global_boolean_var('redirect_on_login') && Url_Helpers::is_wp_login()) {
$dual_login_enabled = Options_Service::get_global_boolean_var('redirect_to_login_v2');
$skip_wp_admin = Options_Service::get_global_boolean_var('skip_wp_admin');
$bypass_key = Options_Service::get_aad_option('redirect_on_login_secret');
$error_page = Options_Service::get_global_string_var('error_page_url');
$secure = ('https' === parse_url(wp_login_url(), PHP_URL_SCHEME));
$cookie_name = defined('WPO_SSO_BYPASS_COOKIE') ? constant('WPO_SSO_BYPASS_COOKIE') : 'wordpress_wpo365_sso_bypass';
$is_post_password = isset($_REQUEST['action']) && $_REQUEST['action'] == 'postpass' && isset($_POST['post_password']) && is_string($_POST['post_password']);
// Bypass when user enters a password to view a password-protected post
if ($is_post_password) {
return true;
}
// Admin has configured to enable SSO for the login page but pre-requisites are not fulfulled.
elseif (isset($_COOKIE[$cookie_name])) {
Log_Service::write_log('DEBUG', __METHOD__ . ' -> A request for the login page will be allowed pass-thru [cookie will be deleted]');
$cookie = $_COOKIE[$cookie_name];
setcookie($cookie_name, '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN, $secure);
unset($_COOKIE[$cookie_name]);
if ($cookie == $bypass_key) {
return true;
}
} elseif ($dual_login_enabled) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Administrator has enabled SSO for the login page but has also enabled the contradicting Dual Login feature');
} elseif ($skip_wp_admin) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Administrator enabled SSO for the login page but has also disabled SSO for WP Admin');
} elseif (empty($bypass_key)) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Administrator has enabled SSO for the login page but has not configured a mandatory secret key to bypass SSO');
} elseif (strlen($bypass_key) < 32) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Administrator has enabled SSO for the login page but the length of the mandatory secret key to bypass SSO is less than 32 characters');
} elseif (empty($error_page)) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Administrator has enabled SSO for the login page but has not configured a mandatory error page');
} elseif (isset($_GET[$bypass_key])) {
Log_Service::write_log('DEBUG', __METHOD__ . ' -> A request for the login page will be allowed pass-thru [cookie will be set]');
setcookie($cookie_name, $bypass_key, 0, COOKIEPATH, COOKIE_DOMAIN, $secure);
return true;
}
// Admin has configured to enable SSO for the login page but no secret key has been detected.
else {
return false;
}
}
// Check if current page is homepage and can be skipped
$public_homepage = Options_Service::get_global_boolean_var('public_homepage');
if (true === $public_homepage && ($GLOBALS['WPO_CONFIG']['url_info']['wp_site_path'] === $GLOBALS['WPO_CONFIG']['url_info']['request_uri'])) {
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Cancelling session validation for home page because public homepage is selected');
return true;
}
// Check if current page is blacklisted and can be skipped
$black_listed_pages = Options_Service::get_global_list_var('pages_blacklist');
// Always add Error Page URL (if configured)
$error_page_url = Options_Service::get_global_string_var('error_page_url');
if (!empty($error_page_url) && WordPress_Helpers::stripos($error_page_url, WordPress_Helpers::rtrim($GLOBALS['WPO_CONFIG']['url_info']['wp_site_url'], '/')) === 0) {
$error_page_url = WordPress_Helpers::rtrim(strtolower($error_page_url), '/');
$error_page_path = WordPress_Helpers::rtrim(parse_url($error_page_url, PHP_URL_PATH), '/');
if (empty($error_page_path) || $error_page_path === $GLOBALS['WPO_CONFIG']['url_info']['wp_site_path']) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Error page URL must be a page and cannot be the root of the current website (' . $error_page_path . ')');
} else {
$black_listed_pages[] = $error_page_path;
}
}
// Always add Custom Login URL (if configured)
$custom_login_url = Options_Service::get_global_string_var('custom_login_url');
if (!empty($custom_login_url)) {
$custom_login_url = WordPress_Helpers::rtrim(strtolower($custom_login_url), '/');
$custom_login_path = WordPress_Helpers::rtrim(parse_url($custom_login_url, PHP_URL_PATH), '/');
if (empty($custom_login_path) || $custom_login_path === $GLOBALS['WPO_CONFIG']['url_info']['wp_site_path']) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Custom Login URL must be a page and cannot be the root of the current website (' . $custom_login_path . ')');
} else {
$black_listed_pages[] = $custom_login_path;
}
}
// Ensure default login path
$default_login_url_path = parse_url(wp_login_url(), PHP_URL_PATH);
if (false === array_search($default_login_url_path, $black_listed_pages)) {
$black_listed_pages[] = $default_login_url_path;
}
// Ensure admin-ajax.php
$admin_ajax_path = 'admin-ajax.php';
if (false === array_search($admin_ajax_path, $black_listed_pages)) {
$black_listed_pages[] = $admin_ajax_path;
}
// Ensure wp-cron.php
$wp_cron_path = 'wp-cron.php';
if (false === array_search($wp_cron_path, $black_listed_pages)) {
$black_listed_pages[] = $wp_cron_path;
}
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Pages Blacklist after error page / custom login has verified');
Log_Service::write_log('DEBUG', $black_listed_pages);
// Check if current page is blacklisted and can be skipped
foreach ($black_listed_pages as $black_listed_page) {
$black_listed_page = WordPress_Helpers::rtrim(strtolower($black_listed_page), '/');
// Filter out empty or mis-configured black page entries
if (empty($black_listed_page) || $black_listed_page == '/' || $black_listed_page == $GLOBALS['WPO_CONFIG']['url_info']['wp_site_path']) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> Black listed page page must be a page and cannot be the root of the current website (' . $black_listed_page . ')');
continue;
}
// Correction after the plugin switched from basename to path based comparison
$starts_with = substr($black_listed_page, 0, 1);
$black_listed_page = $starts_with == '/' || $starts_with == '?' ? $black_listed_page : '/' . $black_listed_page;
// Filter out any attempt to illegally bypass authentication
$illegal_stripos = WordPress_Helpers::stripos($GLOBALS['WPO_CONFIG']['url_info']['request_uri'], '?/');
if ($illegal_stripos !== false && strlen($GLOBALS['WPO_CONFIG']['url_info']['request_uri']) > ($illegal_stripos + 2)) {
Log_Service::write_log('WARN', __METHOD__ . ' -> Serious attempt to try to bypass authentication using an illegal query string combination "?/" (path used: ' . $GLOBALS['WPO_CONFIG']['url_info']['request_uri'] . ')');
break;
} elseif (WordPress_Helpers::stripos($GLOBALS['WPO_CONFIG']['url_info']['request_uri'], $black_listed_page) !== false) {
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Found [' . $black_listed_page . '] thus cancelling session validation for path ' . $GLOBALS['WPO_CONFIG']['url_info']['request_uri']);
return true;
}
}
$scenario = Options_Service::get_global_string_var('auth_scenario');
if (!is_admin() && !is_network_admin() && ($scenario === 'internet' || $scenario === 'internetAuthOnly')) {
$private_pages = Options_Service::get_global_list_var('private_pages');
$login_urls = Url_Helpers::get_login_urls();
// Check if current page is private and cannot be skipped
foreach ($private_pages as $private_page) {
$private_page = WordPress_Helpers::rtrim(strtolower($private_page), '/');
if (empty($private_page)) {
continue;
}
if ($private_page === $GLOBALS['WPO_CONFIG']['url_info']['wp_site_url']) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> The following entry in the Private Pages list is illegal because it is the site url: ' . $private_page);
continue;
}
/**
* @since 9.0
*
* Prevent users from hiding the login page.
*/
if ((!empty($login_urls['default_login_url']) && WordPress_Helpers::stripos($private_page, $login_urls['default_login_url']) !== false) || (!empty($login_urls['custom_login_url']) && WordPress_Helpers::stripos($private_page, $login_urls['custom_login_url']) !== false)) {
Log_Service::write_log('ERROR', __METHOD__ . ' -> The following entry in the Private Pages list is illegal because it is a login url: ' . $private_page);
continue;
}
if (WordPress_Helpers::stripos($GLOBALS['WPO_CONFIG']['url_info']['current_url'], $private_page) === 0) {
/**
* @since 17.0
*
* Authentication may still be skipped when custom rules apply.
*/
if (true === apply_filters('wpo365_skip_authentication', false)) {
return true;
}
return false;
}
}
Log_Service::write_log('DEBUG', __METHOD__ . ' -> Cancelling session validation for page ' . strtolower(basename($_SERVER['PHP_SELF'])) . ' because selected scenario is \'Internet\'');
return true;
}
/**
* @since 10.6
*
* The wpo365_skip_authentication filter hook signals allows its
* subscribers to dynamically add rules that would allow the plugin
* to skip authentication.
*/
if (true === apply_filters('wpo365_skip_authentication', false)) {
return true;
}
return false;
}
/**
* Instead of showing a 404 for private page the user is requested to sign in.
*
* @since 12.x
*/
public static function check_private_pages()
{
if (!Options_Service::get_global_boolean_var('redirect_on_private_page', false)) {
return;
}
Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
// Check is scenario is 'internet' and validation of current page can be skipped
$scenario = Options_Service::get_global_string_var('auth_scenario');
if (!is_admin() && ($scenario === 'internet' || $scenario === 'internetAuthOnly') && !is_user_logged_in()) {
$query_result = \get_queried_object();
if (isset($query_result->post_status) && $query_result->post_status == 'private') {
self::authenticate_request(true);
}
}
}
}
}