Uname: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

403WebShell
403Webshell
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/Tests/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/html/rentals_updated/wp-content/plugins/wpo365-login/Tests/Test_Access_Tokens.php
<?php

namespace Wpo\Tests;

use \Wpo\Core\Extensions_Helpers;
use Wpo\Core\User;
use \Wpo\Core\WordPress_Helpers;

use \Wpo\Services\Access_Token_Service;
use \Wpo\Services\Graph_Service;
use \Wpo\Services\Options_Service;
use \Wpo\Services\Request_Service;
use \Wpo\Services\User_Service;

// Prevent public access to this script
defined('ABSPATH') or die();

if (!class_exists('\Wpo\Tests\Test_Access_Tokens')) {

    class Test_Access_Tokens
    {

        private $delegated_access_token = null;
        private $delegated_access_token_test_result = null;
        private $delegated_static_permissions = array();

        private $application_access_token = null;
        private $application_access_token_test_result = null;
        private $application_static_permissions = array();

        private $no_sso = false;
        private $use_saml = false;
        private $use_b2c = false;
        private $use_ciam = false;
        private $graph_version_beta = false;
        private $extensions = [];
        private $request = null;
        private $wpo_usr = null;
        private $hostname = null;
        private $mail_auth_configuration = null;
        private $tld = '.com';

        public function __construct()
        {
            $this->no_sso = Options_Service::get_global_boolean_var('no_sso');
            $this->use_saml = Options_Service::get_global_boolean_var('use_saml');
            $this->use_b2c = Options_Service::get_global_boolean_var('use_b2c');
            $this->use_ciam = Options_Service::get_global_boolean_var('use_ciam');
            $this->graph_version_beta = Options_Service::get_global_string_var('graph_version') == 'beta';
            $this->extensions = Extensions_Helpers::get_active_extensions();
            $this->tld = !empty($tld = Options_Service::get_global_string_var('tld')) ? $tld : '.com';
        }

        /**
         * INIT
         */
        public function test_initialize()
        {
            $request_service = Request_Service::get_instance();
            $this->request = $request_service->get_request($GLOBALS['WPO_CONFIG']['request_id']);

            if ($this->no_sso) {
                $queries = array();

                if (!empty($_SERVER['QUERY_STRING'])) {
                    parse_str($_SERVER['QUERY_STRING'], $queries);
                }

                if (isset($queries['upn'])) {
                    $upn = filter_var($queries['upn'], FILTER_SANITIZE_EMAIL);
                    $this->wpo_usr = new User();
                    $this->wpo_usr->upn = $upn;
                    $this->wpo_usr->oid = $upn;
                }
            } else {
                $this->wpo_usr = $this->request->get_item('wpo_usr');
            }

            $this->get_delegated_access_token();
            $this->get_application_access_token();
        }

        /**
         * ACCESS TOKENS
         */
        public function test_access_token_delegated()
        {

            if ($this->no_sso || $this->use_saml || $this->use_b2c) {
                return;
            }

            if (!$this->delegated_access_token_test_result->passed) {
                return $this->delegated_access_token_test_result;
            }
        }

        /**
         * ACCESS TOKENS -> PERMISSIONS
         */
        public function test_access_token_static_permissions_openid()
        {

            if ($this->no_sso || $this->use_saml || $this->use_b2c || !$this->delegated_access_token_test_result->passed) {
                return;
            }

            return $this->check_static_permission(
                $this->delegated_access_token,
                'openid',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_BLOCKING,
                Test_Result::CAPABILITY_OIC_SSO,
                'and as a result the plugin may not be able to request (OpenID Connect related) ID tokens. Please add the following API Permission (for the (Azure AD) <em>registered application</em> in Azure Portal): Microsoft Graph > Delegated > openid.',
                'https://docs.wpo365.com/article/154-aad-single-sign-for-wordpress-using-auth-code-flow'
            );
        }

        /**
         * ACCESS TOKENS -> PERMISSIONS
         */
        public function test_access_token_static_permissions_email()
        {

            if ($this->no_sso || $this->use_saml || $this->use_b2c || !$this->delegated_access_token_test_result->passed) {
                return;
            }

            return $this->check_static_permission(
                $this->delegated_access_token,
                'email',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_CRITICAL,
                Test_Result::CAPABILITY_OIC_SSO,
                'and as a result the plugin may fail to match an Office 365 / Azure AD user by his / her email address. Please add the following API Permission (for the (Azure AD) <em>registered application</em> in Azure Portal): Microsoft Graph > Delegated > offline_access.',
                'https://docs.wpo365.com/article/154-aad-single-sign-for-wordpress-using-auth-code-flow'
            );
        }

        /**
         * ACCESS TOKENS -> PERMISSIONS
         */
        public function test_access_token_static_permissions_user_read()
        {

            if ($this->no_sso || $this->use_saml || $this->use_b2c || !$this->delegated_access_token_test_result->passed) {
                return;
            }

            return $this->check_static_permission(
                $this->delegated_access_token,
                'user.read',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_BLOCKING,
                Test_Result::CAPABILITY_OIC_SSO,
                'and as a result the plugin will not be able to sign in as a user. Please add the following API Permission (for the (Azure AD) <em>registered application</em> in Azure Portal): Microsoft Graph > Delegated > User.Read.',
                'https://docs.wpo365.com/article/154-aad-single-sign-for-wordpress-using-auth-code-flow'
            );
        }

        /**
         * ACCESS TOKENS -> REFRESH TOKEN
         */
        public function test_refresh_token_delegated()
        {

            if ($this->no_sso || $this->use_saml || $this->use_b2c || !$this->delegated_access_token_test_result->passed) {
                return;
            }

            $test_result = new Test_Result('Access token for <em>delegated</em> permissions includes a refresh token.', Test_Result::CAPABILITY_ACCESS_TOKENS, Test_Result::SEVERITY_CRITICAL);
            $test_result->passed = true;

            if (!property_exists($this->delegated_access_token, 'refresh_token')) {
                $test_result->passed = false;
                $test_result->message = 'Access token for <em>delegated</em> permissions does not include a refresh token. Please add the following API Permission (for the (Azure AD) <em>registered application</em> in Azure Portal): Microsoft Graph > Delegated > offline_access.';
                $test_result->more_info = '';
                return $test_result;
            }

            return $test_result;
        }

        /**
         * ACCESS TOKENS -> GRAPH VERION
         */
        public function test_graph_version()
        {
            $test_result = new Test_Result('<em>Beta</em> version of Microsoft Graph selected.', Test_Result::CAPABILITY_CONFIG, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            if (!$this->graph_version_beta) {
                $test_result->passed = false;
                $test_result->message = 'Update the Microsoft Graph version to <em>beta</em> on the plugin\'s <a href=\"#integration\">integration</a> page.';
                $test_result->more_info = 'https://docs.wpo365.com/article/67-microsoft-graph-version';
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'graphVersion' => 'beta',
                        ),
                    ),
                );
                return $test_result;
            }

            return $test_result;
        }

        /**
         * PROFILE+ | DELEGATED
         */
        public function test_profile_plus_delegated()
        {

            // Skip test if using SAML
            if ($this->no_sso || $this->use_saml || $this->use_b2c) {
                return;
            }

            $test_result = new Test_Result('Get a user\'s basic profile (first, last and display name and email address) with <em>delegated</em> permissions.', Test_Result::CAPABILITY_PROFILE_PLUS, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            // Check if suitable extensions can be found
            $suitable_extensions = \array_flip(
                array(
                    'wpo365-login-premium/wpo365-login.php',
                    'wpo365-login-intranet/wpo365-login.php',
                    'wpo365-intranet-5y/wpo365-intranet-5y.php',
                    'wpo365-sync-5y/wpo365-sync-5y.php',
                    'wpo365-login-plus/wpo365-login.php',
                    'wpo365-mail/wpo365-mail.php',
                    'wpo365-login-professional/wpo365-login.php',
                    'wpo365-avatar/wpo365-avatar.php',
                    'wpo365-custom-fields/wpo365-custom-fields.php',
                    'wpo365-groups/wpo365-groups.php',
                    'wpo365-roles-access/wpo365-roles-access.php',
                    'wpo365-scim/wpo365-scim.php',
                    'wpo365-customers/wpo365-customers.php',
                    'wpo365-integrate/wpo365-integrate.php',
                    'wpo365-pro/wpo365-pro.php',
                )
            );

            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the PROFILE+ feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-extensions/';
                return $test_result;
            }

            // Check if access token with delegated permissions can be found
            if (!$this->delegated_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->delegated_access_token_test_result->message;
                $test_result->more_info = $this->delegated_access_token_test_result->more_info;
                return $test_result;
            }

            // Check if access token has appropriate permissions
            $test_result_profile = $this->check_static_permission(
                $this->delegated_access_token,
                'profile',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_PROFILE_PLUS,
                'and as a result the ID token will not contain basic profile information.',
                'https://docs.wpo365.com/article/154-aad-single-sign-for-wordpress-using-auth-code-flow'
            );

            if (!$test_result_profile->passed) {
                $test_result->passed = $test_result_profile->passed;
                $test_result->message = $test_result_profile->message;
                $test_result->more_info = $test_result_profile->more_info;
                return $test_result;
            }

            // Check if a user can be retrieved from Microsoft Graph
            if (\class_exists('\Wpo\Services\User_Details_Service')) {

                if (!empty($this->wpo_usr) && !empty($this->wpo_usr->oid)) {
                    $graph_user = \Wpo\Services\User_Details_Service::get_graph_user($this->wpo_usr->oid, false, true, true);
                    $test_result->data = $graph_user;
                }

                if (is_wp_error($test_result->data) || empty($test_result->data) || !isset($test_result->data['@odata.context'])) {
                    $test_result->passed = false;
                    $test_result->message = 'Could not retrieve a user resource from Microsoft Graph for current user. Click "View" for details.';
                } else {
                    // Save wpo_usr for later use
                    $this->wpo_usr = User_Service::user_from_graph_user($test_result->data);
                }
            }

            return $test_result;
        }

        /**
         * PROFILE+ | APPLICATION
         */
        public function test_profile_plus_application()
        {

            // Skip test if using B2C and instead recommend to use ID token as source for profile fields
            if ($this->use_b2c) {
                return;
            }

            $test_result = new Test_Result('Get a user\'s basic profile (first, last and display name and email address) with <em>application</em> permissions.', Test_Result::CAPABILITY_PROFILE_PLUS, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            // Check if suitable extension can be found
            $suitable_extensions = \array_flip(
                array(
                    'wpo365-login-premium/wpo365-login.php',
                    'wpo365-login-intranet/wpo365-login.php',
                    'wpo365-intranet-5y/wpo365-intranet-5y.php',
                    'wpo365-sync-5y/wpo365-sync-5y.php',
                    'wpo365-login-plus/wpo365-login.php',
                    'wpo365-mail/wpo365-mail.php',
                    'wpo365-login-professional/wpo365-login.php',
                    'wpo365-avatar/wpo365-avatar.php',
                    'wpo365-custom-fields/wpo365-custom-fields.php',
                    'wpo365-groups/wpo365-groups.php',
                    'wpo365-roles-access/wpo365-roles-access.php',
                    'wpo365-scim/wpo365-scim.php',
                    'wpo365-customers/wpo365-customers.php',
                    'wpo365-integrate/wpo365-integrate.php',
                    'wpo365-pro/wpo365-pro.php',
                )
            );

            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the PROFILE+ feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-extensions/';
                return $test_result;
            }

            // Check if access token with application permissions can be found
            if (!$this->application_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->application_access_token_test_result->message;
                $test_result->more_info = $this->application_access_token_test_result->more_info;
                return $test_result;
            }

            // Check if access token has appropriate permissions
            $test_result_user_read_all = $this->check_static_permission(
                $this->application_access_token,
                'user.read.all',
                'application',
                $this->application_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_PROFILE_PLUS,
                'and as a result the plugin cannot independently from the logged-in user retrieve user profile information from Azure AD using Microsoft Graph.',
                'https://docs.wpo365.com/article/23-integration'
            );

            if (!$test_result_user_read_all->passed) {
                $test_result->passed = $test_result_user_read_all->passed;
                $test_result->message = $test_result_user_read_all->message;
                $test_result->more_info = $test_result_user_read_all->more_info;
                return $test_result;
            }

            // Check if a user can be retrieved from Microsoft Graph
            if (\class_exists('\Wpo\Services\User_Details_Service')) {

                if (!empty($this->wpo_usr) && !empty($this->wpo_usr->oid)) {
                    $graph_user = \Wpo\Services\User_Details_Service::get_graph_user($this->wpo_usr->oid, false, false, true);
                    $test_result->data = $graph_user;
                }

                if (is_wp_error($test_result->data) || empty($test_result->data) || !isset($test_result->data['@odata.context'])) {
                    $test_result->passed = false;
                    $test_result->message = 'Could not retrieve a user resource from Microsoft Graph for current user. Click "View" for details.';
                } else {
                    // Save wpo_usr for later use
                    $this->wpo_usr = User_Service::user_from_graph_user($test_result->data);
                }
            }

            return $test_result;
        }

        /**
         * MAIL
         */
        public function test_mail()
        {
            $test_result = new Test_Result('Send WordPress emails using Microsoft Graph.', Test_Result::CAPABILITY_MAIL, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            if (empty(Options_Service::get_global_boolean_var('use_graph_mailer'))) {
                $test_result->passed = false;
                $test_result->message = "Enable sending WordPress emails using Microsoft Graph on the plugin's <a href=\"#mail\">Mail</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'useGraphMailer' => true,
                        ),
                    ),
                );
                $test_result->more_info = 'https://docs.wpo365.com/article/108-sending-wordpress-emails-using-microsoft-graph';
                return $test_result;
            }

            $this->mail_auth_configuration = \Wpo\Mail\Mail_Authorization_Helpers::get_mail_auth_configuration(false, false);

            if ($this->mail_auth_configuration->delegated_authorized && $this->mail_auth_configuration->app_only_authorized) {
                $test_result->passed = false;
                $test_result->message = 'It appears that you have configured both <strong>application</strong> and <strong>delegated</strong> permissions for <em>Mail.Send</em>.  
                    If you do not need the plugin\'s ability to send emails from multiple different accounts then it is strongly recommended that you remove 
                    <strong>application</strong> permissions for <em>Mail.Send</em>. The reason behind this recommendation is the general trend to configure as few and as low permissions as 
                    possible. <strong>Application</strong> <em>Mail.Send</em> permissions allow the plugin to send emails as any user in the organization whereas <strong>
                    delegated</strong> <em>Mail.Send</em> permissions reduce this to a single account.<br/><div style="color: #0078D4;">If you just changed <strong>delegated</strong> API Permissions in Azure AD 
                    then please re-authorize the mail user on the plugin\'s <strong>Mail</strong> configuration page and before runing the Plugin self-test again.</div>';
                $test_result->more_info = 'https://docs.wpo365.com/article/108-sending-wordpress-emails-using-microsoft-graph';
                return $test_result;
            }

            if ($this->mail_auth_configuration->delegated_authorized && $this->mail_auth_configuration->has_refresh_token) {
                return $test_result;
            }

            if (!$this->mail_auth_configuration->delegated_authorized) {
                $test_result->passed = false;
                $test_result->message = sprintf(
                    'You have configured the WPO365 plugin to send WordPress emails using Microsoft Graph but you have not configured <strong>delegated</strong> <em>Mail.Send</em> 
                    permissions and authorized an account to send mail from (using the <strong>Authorize</strong> function on the plugin\'s <a href="#mail">Mail</a> configuration page). %s',
                    !$this->mail_auth_configuration->app_only_authorized ? '' : 'However, it appears that you have configured <strong>application</strong> <em>Mail.Send</em> permissions.  
                    If you do not need the plugin\'s ability to send emails from multiple different accounts then it is strongly recommended that you remove 
                    <strong>application</strong> permissions for <em>Mail.Send</em>. The reason behind this recommendation is the general trend to configure as few and as low permissions as 
                    possible. <strong>Application</strong> <em>Mail.Send</em> permissions allow the plugin to send emails as any user in the organization whereas <strong>
                    delegated</strong><em>Mail.Send</em> permissions reduce this to a single account.'
                );
                $test_result->more_info = 'https://docs.wpo365.com/article/108-sending-wordpress-emails-using-microsoft-graph';
                return $test_result;
            }

            if (!$this->mail_auth_configuration->has_refresh_token) {
                $test_result->passed = false;
                $test_result->message = 'You have configured the WPO365 plugin to send WordPress emails using Microsoft Graph but you have not configured <strong>delegated</strong> 
                    <em>offline_access</em> permissions. As a result, the WPO365 plugin cannot refresh the access token silently and thus will fail to send emails as soon as the access token 
                    is expired (by default after 60 minutes).';
                $test_result->more_info = 'https://docs.wpo365.com/article/108-sending-wordpress-emails-using-microsoft-graph';
                return $test_result;
            }
        }

        public function test_mail_shared()
        {
            $test_result = new Test_Result('Send WordPress emails from a Shared Mailbox using Microsoft Graph.', Test_Result::CAPABILITY_MAIL, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            if (empty(Options_Service::get_global_boolean_var('use_graph_mailer'))) {
                return;
            }

            // Check if suitable extension can be found
            $suitable_extensions = \array_flip(
                array(
                    'wpo365-mail/wpo365-mail.php',
                    'wpo365-login-premium/wpo365-login.php',
                    'wpo365-login-intranet/wpo365-login.php',
                    'wpo365-intranet-5y/wpo365-intranet-5y.php',
                    'wpo365-sync-5y/wpo365-sync-5y.php',
                    'wpo365-integrate/wpo365-integrate.php',
                    'wpo365-pro/wpo365-pro.php',
                )
            );

            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                return;
            }

            if ($this->mail_auth_configuration === null) {
                $this->mail_auth_configuration = \Wpo\Mail\Mail_Authorization_Helpers::get_mail_auth_configuration(false, false);
            }

            $mail_application_id = Options_Service::get_mail_option('mail_application_id');

            if (empty($mail_application_id)) {
                $mail_application_id = Options_Service::get_aad_option('application_id');
            }

            if (empty($this->mail_auth_configuration->delegated_shared_authorized) && empty($this->mail_auth_configuration->app_only_authorized)) {
                $test_result->passed = false;
                $test_result->message = "You have not granted delegated <em>Mail.Send.Shared</em> permissions in Azure AD for App registration with ID 
                                         <strong>$mail_application_id</strong> (or alternatively granted application-level <em>Mail.Send</em> permissions) 
                                         and as a result you cannot send WordPress emails from a Microsoft 365 Shared Mailbox. You can safely ignore this 
                                         if you do not wish to send WordPress emails from such a Shared Mailbox.<br/><div style=\"color: #0078D4;\">If 
                                         you just changed <strong>delegated</strong> API Permissions in Azure AD then please re-authorize the mail user 
                                         on the plugin\'s <strong>Mail</strong> configuration page before running the Plugin self-test again.</div>";
                $test_result->more_info = 'https://docs.wpo365.com/article/108-sending-wordpress-emails-using-microsoft-graph';
                return $test_result;
            }

            return $test_result;
        }

        public function test_mail_large_attachments()
        {
            $test_result = new Test_Result('Send WordPress emails with attachments larger than 3 MB using Microsoft Graph.', Test_Result::CAPABILITY_MAIL, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            if (empty(Options_Service::get_global_boolean_var('use_graph_mailer'))) {
                return;
            }

            // Check if suitable extension can be found
            $suitable_extensions = \array_flip(
                array(
                    'wpo365-mail/wpo365-mail.php',
                    'wpo365-login-premium/wpo365-login.php',
                    'wpo365-login-intranet/wpo365-login.php',
                    'wpo365-intranet-5y/wpo365-intranet-5y.php',
                    'wpo365-sync-5y/wpo365-sync-5y.php',
                    'wpo365-integrate/wpo365-integrate.php',
                    'wpo365-pro/wpo365-pro.php',
                )
            );

            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                return;
            }

            if (!class_exists('\Wpo\Mail\Mail_Attachments')) {
                $test_result->passed = false;
                $test_result->message = "There is a newer version available for your premium WPO365 plugin(s) that supports sending WordPress emails with attachments larger than 3 MB. Please update now.";
                $test_result->more_info = 'https://docs.wpo365.com/article/13-update-the-wpo365-plugin-to-the-latest-version';
                return $test_result;
            }

            if ($this->mail_auth_configuration === null) {
                $this->mail_auth_configuration = \Wpo\Mail\Mail_Authorization_Helpers::get_mail_auth_configuration(false, false);
            }

            $mail_application_id = Options_Service::get_mail_option('mail_application_id');

            if (empty($mail_application_id)) {
                $mail_application_id = Options_Service::get_aad_option('application_id');
            }

            if (empty($this->mail_auth_configuration->delegated_readwrite_authorized) && empty($this->mail_auth_configuration->app_only_readwrite_authorized)) {
                $test_result->passed = false;
                $test_result->message = "You have not granted delegated <em>Mail.ReadWrite</em> permissions in Azure AD for App registration with ID 
                                         <strong>$mail_application_id</strong> (or alternatively granted application-level <em>Mail.ReadWrite</em> 
                                         permissions) and as a result you cannot send attachments larger than 3 MB from WordPress using Microsoft Graph.
                                         <br/><div style=\"color: #0078D4;\">If you just changed <strong>delegated</strong> API Permissions in Azure AD then 
                                         please re-authorize the mail user on the plugin\'s <strong>Mail</strong> configuration page before running 
                                         the Plugin self-test again.</div>";
                $test_result->more_info = 'https://docs.wpo365.com/article/108-sending-wordpress-emails-using-microsoft-graph';
                return $test_result;
            }

            return $test_result;
        }

        public function test_mail_large_attachments_shared()
        {
            $test_result = new Test_Result('Send WordPress emails with attachments larger than 3 MB using Microsoft Graph from a Shared Mailbox.', Test_Result::CAPABILITY_MAIL, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            if (empty(Options_Service::get_global_boolean_var('use_graph_mailer'))) {
                return;
            }

            // Check if suitable extension can be found
            $suitable_extensions = \array_flip(
                array(
                    'wpo365-mail/wpo365-mail.php',
                    'wpo365-login-premium/wpo365-login.php',
                    'wpo365-login-intranet/wpo365-login.php',
                    'wpo365-intranet-5y/wpo365-intranet-5y.php',
                    'wpo365-sync-5y/wpo365-sync-5y.php',
                    'wpo365-integrate/wpo365-integrate.php',
                    'wpo365-pro/wpo365-pro.php',
                )
            );

            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                return;
            }

            if (!class_exists('\Wpo\Mail\Mail_Attachments')) {
                $test_result->passed = false;
                $test_result->message = "There is a newer version available for your premium WPO365 plugin(s) that supports sending WordPress emails with attachments larger than 3 MB. Please update now.";
                $test_result->more_info = 'https://docs.wpo365.com/article/13-update-the-wpo365-plugin-to-the-latest-version';
                return $test_result;
            }

            if ($this->mail_auth_configuration === null) {
                $this->mail_auth_configuration = \Wpo\Mail\Mail_Authorization_Helpers::get_mail_auth_configuration(false, false);
            }

            $mail_application_id = Options_Service::get_mail_option('mail_application_id');

            if (empty($mail_application_id)) {
                $mail_application_id = Options_Service::get_aad_option('application_id');
            }

            if (empty($this->mail_auth_configuration->delegated_readwrite_shared_authorized) && empty($this->mail_auth_configuration->app_only_readwrite_authorized)) {
                $test_result->passed = false;
                $test_result->message = "You have not granted delegated <em>Mail.ReadWrite.Shared</em> permissions in Azure AD for App registration with ID 
                                         <strong>$mail_application_id</strong> (or alternatively granted application-level <em>Mail.ReadWrite</em> permissions) 
                                         and as a result you cannot send attachments larger than 3 MB from WordPress using Microsoft Graph from a Microsoft 365 Shared 
                                         Mailbox. You can safely ignore this if you do not wish to send WordPress emails from such a Shared Mailbox.
                                         <br/><div style=\"color: #0078D4;\">If you just changed <strong>delegated</strong> API Permissions in Azure AD then 
                                         please re-authorize the mail user on the plugin\'s <strong>Mail</strong> configuration page before running
                                         the Plugin self-test again.</div>";
                $test_result->more_info = 'https://docs.wpo365.com/article/108-sending-wordpress-emails-using-microsoft-graph';
                return $test_result;
            }

            return $test_result;
        }

        /**
         * ROLES + ACCESS | DELEGATED
         */
        public function test_roles_access_delegated()
        {

            // Skip if using SAML
            if ($this->no_sso || $this->use_saml || $this->use_b2c) {
                return;
            }

            $test_result = new Test_Result('Get a user\'s Azure AD group memberships with <em>delegated</em> permissions.', Test_Result::CAPABILITY_ROLES_ACCESS, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $suitable_extensions = \array_flip(array(
                'wpo365-login-premium/wpo365-login.php',
                'wpo365-login-intranet/wpo365-login.php',
                'wpo365-roles-access/wpo365-roles-access.php',
                'wpo365-intranet-5y/wpo365-intranet-5y.php',
                'wpo365-sync-5y/wpo365-sync-5y.php',
                'wpo365-integrate/wpo365-integrate.php',
                'wpo365-pro/wpo365-pro.php',
            ));

            // Check if suitable extensions can be found
            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the ROLES + ACCESS / AUDIENCES feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-extensions/';
                return $test_result;
            }

            // Check if suitable access token with delegated permissions can be found
            if (!$this->delegated_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->delegated_access_token_test_result->message;
                $test_result->more_info = $this->delegated_access_token_test_result->more_info;
                return $test_result;
            }

            // Check if access token has appropriate permissions
            $test_result_groupmember_read_all = $this->check_static_permission(
                $this->delegated_access_token,
                'groupmember.read.all',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_ROLES_ACCESS,
                'and as a result the plugin cannot retrieve a user\'s Azure AD group memberships.',
                'https://docs.wpo365.com/article/23-integration'
            );

            // Check if access token has appropriate permissions
            $test_result_group_read_all = $this->check_static_permission(
                $this->delegated_access_token,
                'group.read.all',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_ROLES_ACCESS,
                'and as a result the plugin cannot retrieve a user\'s Azure AD group memberships.',
                'https://docs.wpo365.com/article/23-integration'
            );

            if (!$test_result_groupmember_read_all->passed && $test_result_group_read_all->passed) {
                $application_id = Options_Service::get_aad_option('application_id');
                $test_result->passed = false;
                $test_result->message = "Static permission <strong>Group.Read.All</strong> has been configured for the (Azure AD) <em>registered application</em> with ID <strong>$application_id</strong> but starting with v18.0 it is strongly recommended to instead configure <strong>GroupMember.Read.All</strong> API Permission (and to remove <em>Group.Read.All</em>).";
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                return $test_result;
            }

            if (!$test_result_groupmember_read_all->passed) {
                $test_result->passed = $test_result_groupmember_read_all->passed;
                $test_result->message = $test_result_groupmember_read_all->message;
                $test_result->more_info = $test_result_groupmember_read_all->more_info;
                return $test_result;
            }

            // Check if a user's member groups can be retrieved from Microsoft Graph
            if (!empty($this->wpo_usr) && \class_exists('\Wpo\Services\User_Aad_Groups_Service')) {

                $test_result->data = \Wpo\Services\User_Aad_Groups_Service::get_aad_groups($this->wpo_usr, true, true);

                if (!isset($test_result->data['response_code']) || $test_result->data['response_code'] != 200) {
                    $test_result->passed = false;
                    $test_result->message = 'Failed to retrieve the Azure AD groups for current user from Microsoft Graph. Inspect the data that was received below for a possible reason.';
                }
            } else {
                $test_result->passed = false;
                $test_result->message = 'Failed to retrieve the Azure AD groups for current user from Microsoft Graph due to earliers errors.';
            }

            return $test_result;
        }

        /**
         * ROLES + ACCESS | APPLICATION
         */
        public function test_roles_access_application()
        {

            $test_result = new Test_Result('Get a user\'s Azure AD group memberships with <em>application</em> permissions.', Test_Result::CAPABILITY_ROLES_ACCESS, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $suitable_extensions = \array_flip(array(
                'wpo365-login-premium/wpo365-login.php',
                'wpo365-login-intranet/wpo365-login.php',
                'wpo365-roles-access/wpo365-roles-access.php',
                'wpo365-intranet-5y/wpo365-intranet-5y.php',
                'wpo365-sync-5y/wpo365-sync-5y.php',
                'wpo365-integrate/wpo365-integrate.php',
                'wpo365-pro/wpo365-pro.php',
            ));

            // Check if suitable extensions can be found
            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the ROLES + ACCESS / AUDIENCES feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-extensions/';
                return $test_result;
            }

            // Check if access token with application permissions can be found
            if (!$this->application_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->application_access_token_test_result->message;
                $test_result->more_info = $this->application_access_token_test_result->more_info;
                return $test_result;
            }

            // Check if access token has appropriate permissions
            $test_result_groupmember_read_all = $this->check_static_permission(
                $this->application_access_token,
                'groupmember.read.all',
                'application',
                $this->application_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_ROLES_ACCESS,
                'and as a result the plugin cannot retrieve a user\'s Azure AD group memberships.',
                'https://docs.wpo365.com/article/23-integration'
            );

            // Check if access token has appropriate permissions
            $test_result_group_read_all = $this->check_static_permission(
                $this->application_access_token,
                'group.read.all',
                'application',
                $this->application_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_ROLES_ACCESS,
                'and as a result the plugin cannot retrieve a user\'s Azure AD group memberships.',
                'https://docs.wpo365.com/article/23-integration'
            );

            if (!$test_result_groupmember_read_all->passed && $test_result_group_read_all->passed) {
                $application_id = Options_Service::get_aad_option('app_only_application_id');
                $test_result->passed = false;
                $test_result->message = "Static permission <strong>Group.Read.All</strong> has been configured for the (Azure AD) <em>registered application</em> with ID <strong>$application_id</strong> but starting with v18.0 it is strongly recommended to instead configure <strong>GroupMember.Read.All</strong> API Permission (and to remove <em>Group.Read.All</em>).";
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                return $test_result;
            }

            if (!$test_result_groupmember_read_all->passed) {
                $test_result->passed = $test_result_groupmember_read_all->passed;
                $test_result->message = $test_result_groupmember_read_all->message;
                $test_result->more_info = $test_result_groupmember_read_all->more_info;
                return $test_result;
            }

            // Check if a user's member groups can be retrieved from Microsoft Graph
            if (!empty($this->wpo_usr) && \class_exists('\Wpo\Services\User_Aad_Groups_Service')) {

                $test_result->data = \Wpo\Services\User_Aad_Groups_Service::get_aad_groups($this->wpo_usr, true, true);

                if (!isset($test_result->data['response_code']) || $test_result->data['response_code'] != 200) {
                    $test_result->passed = false;
                    $test_result->message = 'Failed to retrieve the Azure AD groups for current user from Microsoft Graph. Inspect the data that was received below for a possible reason.';
                }
            } else {
                $test_result->passed = false;
                $test_result->message = 'Failed to retrieve the Azure AD groups for current user from Microsoft Graph due to earliers errors.';
            }

            return $test_result;
        }

        /**
         * AVATAR | DELEGATED
         */
        public function test_avatar_delegated()
        {

            // Skip if using SAML
            if ($this->no_sso || $this->use_saml || $this->use_b2c) {
                return;
            }

            $test_result = new Test_Result('Fetch a user\'s Microsoft 365 profile photo with <em>delegated</em> permissions.', Test_Result::CAPABILITY_AVATAR, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $suitable_extensions = \array_flip(array(
                'wpo365-login-premium/wpo365-login.php',
                'wpo365-login-intranet/wpo365-login.php',
                'wpo365-avatar/wpo365-avatar.php',
                'wpo365-intranet-5y/wpo365-intranet-5y.php',
                'wpo365-sync-5y/wpo365-sync-5y.php',
                'wpo365-integrate/wpo365-integrate.php',
                'wpo365-pro/wpo365-pro.php',
            ));

            // Check if suitable extensions can be found
            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the AVATAR feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-extensions/';
                return $test_result;
            }

            // Check if avatar is on
            if (!Options_Service::get_global_boolean_var('use_avatar')) {
                $test_result->passed = false;
                $test_result->message = "Enable the retrieval of a user's profile image as WordPress avatar on the plugin's <a href=\"#userSync\">User sync</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'useAvatar' => true,
                        ),
                    ),
                );
                $test_result->more_info = 'https://docs.wpo365.com/article/96-microsoft-365-profile-picture-as-wp-avatar';
                return $test_result;
            }

            // Check if suitable access token with delegated permissions can be found
            if (!$this->delegated_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->delegated_access_token_test_result->message;
                $test_result->more_info = $this->delegated_access_token_test_result->more_info;
                return $test_result;
            }

            // Check if access token has appropriate permissions
            $test_result_user_read_all = $this->check_static_permission(
                $this->delegated_access_token,
                'user.read',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_AVATAR,
                'and as a result the plugin cannot retrieve a user\'s Azure AD profile photo.',
                'https://docs.wpo365.com/article/96-microsoft-365-profile-picture-as-wp-avatar'
            );

            if (!$test_result_user_read_all->passed) {
                $test_result->passed = $test_result_user_read_all->passed;
                $test_result->message = $test_result_user_read_all->message;
                $test_result->more_info = $test_result_user_read_all->more_info;
                return $test_result;
            }

            // Check if a user's profile photo can be retrieved from Microsoft Graph
            if (!empty($this->wpo_usr) && !empty($this->wpo_usr->oid) && \class_exists('\Wpo\Services\Avatar_Service')) {

                $data = Graph_Service::fetch('/users/' . $this->wpo_usr->oid . '/photo/$value', 'GET', true, array('Accept: application/json;odata.metadata=minimal'), true, false, '', 'https://graph.microsoft.com/User.Read');

                if (!\is_wp_error($data) && isset($data['response_code'])) {
                    $test_result->data = array('imageUri' => 'data:image/jpeg;base64,' . base64_encode($data['payload']));
                } else {
                    $test_result->data = $data;
                    $test_result->passed = false;
                    $test_result->message = 'Failed to retrieve a profile photo for the current user. Inspect the data that was received below for a possible reason.';
                    $test_result->more_info = 'https://docs.wpo365.com/article/96-microsoft-365-profile-picture-as-wp-avatar';
                }
            } else {
                $test_result->passed = false;
                $test_result->message = 'Failed to retrieve a profile photo for current user from Microsoft Graph due to earliers errors.';
                $test_result->more_info = 'https://docs.wpo365.com/article/96-microsoft-365-profile-picture-as-wp-avatar';
            }

            return $test_result;
        }

        /**
         * AVATAR | APPLICATION
         */
        public function test_avatar_application()
        {

            $test_result = new Test_Result('Fetch a user\'s Microsoft 365 profile photo with <em>application</em> permissions.', Test_Result::CAPABILITY_AVATAR, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $suitable_extensions = \array_flip(array(
                'wpo365-login-premium/wpo365-login.php',
                'wpo365-login-intranet/wpo365-login.php',
                'wpo365-avatar/wpo365-avatar.php',
                'wpo365-intranet-5y/wpo365-intranet-5y.php',
                'wpo365-sync-5y/wpo365-sync-5y.php',
                'wpo365-integrate/wpo365-integrate.php',
                'wpo365-pro/wpo365-pro.php',
            ));

            // Check if suitable extensions can be found
            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the AVATAR feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-extensions/';
                return $test_result;
            }

            // Check if avatar is on
            if (!Options_Service::get_global_boolean_var('use_avatar')) {
                $test_result->passed = false;
                $test_result->message = "Enable the retrieval of a user's profile image as WordPress avatar on the plugin's <a href=\"#userSync\">User sync</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'useAvatar' => true,
                        ),
                    ),
                );
                $test_result->more_info = 'https://docs.wpo365.com/article/96-microsoft-365-profile-picture-as-wp-avatar';
                return $test_result;
            }

            // Check if access token with application permissions can be found
            if (!$this->application_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->application_access_token_test_result->message;
                $test_result->more_info = $this->application_access_token_test_result->more_info;
                return $test_result;
            }

            // Check if access token has appropriate permissions
            $test_result_user_read_all = $this->check_static_permission(
                $this->application_access_token,
                'user.read.all',
                'application',
                $this->application_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_AVATAR,
                'and as a result the plugin cannot independently from the currently logged-in user retrieve a user\'s Azure AD profile photo.',
                'https://docs.wpo365.com/article/96-microsoft-365-profile-picture-as-wp-avatar'
            );

            if (!$test_result_user_read_all->passed) {
                $test_result->passed = $test_result_user_read_all->passed;
                $test_result->message = $test_result_user_read_all->message;
                $test_result->more_info = $test_result_user_read_all->more_info;
                return $test_result;
            }

            // Check if a user's profile photo can be retrieved from Microsoft Graph
            if (!empty($this->wpo_usr) && !empty($this->wpo_usr->oid) && \class_exists('\Wpo\Services\Avatar_Service')) {

                $data = Graph_Service::fetch('/users/' . $this->wpo_usr->oid . '/photo/$value', 'GET', true, array('Accept: application/json;odata.metadata=minimal'), false, false, '', 'https://graph.microsoft.com/User.Read.All');

                if (!\is_wp_error($data) && isset($data['response_code']) && $data['response_code'] == 200) {
                    $test_result->data = array('imageUri' => 'data:image/jpeg;base64,' . base64_encode($data['payload']));
                } else {
                    $test_result->data = $data;
                    $test_result->passed = false;
                    $test_result->message = 'Failed to retrieve a profile photo for the current user. Inspect the data that was received below for a possible reason.';
                    $test_result->more_info = 'https://docs.wpo365.com/article/96-microsoft-365-profile-picture-as-wp-avatar';
                }
            } else {
                $test_result->passed = false;
                $test_result->message = 'Failed to retrieve a profile photo for current user from Microsoft Graph due to earliers errors.';
                $test_result->more_info = 'https://docs.wpo365.com/article/96-microsoft-365-profile-picture-as-wp-avatar';
            }

            return $test_result;
        }

        /**
         * SYNC | CRON DISABLED
         */
        public function test_sync_cron()
        {

            // Check if sync is en
            $sync_enabled = Options_Service::get_global_boolean_var('enable_user_sync');

            if (empty($sync_enabled)) {
                return;
            }

            $test_result = new Test_Result('WP-Cron is disabled.', Test_Result::CAPABILITY_SYNC, Test_Result::SEVERITY_CRITICAL);
            $test_result->passed = true;

            // Check if cron has been disabled
            if (!defined('DISABLE_WP_CRON') || empty(\constant('DISABLE_WP_CRON'))) {
                $test_result->passed = false;
                $test_result->message = 'Since WP-Cron does not run continiously, it is strongly recommended that you <em>disable WordPress CRON</em> by adding the line <em>define( \'DISABLE_WP_CRON\', true );</em> to your wp-config.php file and configure an external task scheduling service to trigger WP-Cron at regular intervals (e.g. every minute).';
                $test_result->more_info = 'https://docs.wpo365.com/article/135-hooking-wp-cron-into-a-task-scheduling-service';
                return $test_result;
            }

            // Check if wp-cron.php has been added to the list of pages freed from authentication

            $allowed_urls = Options_Service::get_global_list_var('pages_blacklist');
            $found = false;

            foreach ($allowed_urls as $url) {

                if (false !== WordPress_Helpers::stripos($url, 'wp-cron.php')) {
                    $found = true;
                    break;
                }
            }

            if (empty($found)) {
                $test_result->passed = false;
                $test_result->message = 'Since you disabled WP-Cron you must add "wp-cron.php" to the list of pages freed from authentication on the plugin\'s <a href="#singleSignOn">Single Sign-on</a> configuration page to be able to trigger WP-Cron at regular intervals from an extenal task scheduling service.';
                $test_result->more_info = 'https://docs.wpo365.com/article/135-hooking-wp-cron-into-a-task-scheduling-service';
                $test_result->fix = array(
                    array(
                        'op' => 'add',
                        'value' => array(
                            'pagesBlacklist' => 'wp-cron.php',
                        ),
                    ),
                );
                return $test_result;
            }

            return $test_result;
        }

        /**
         * SYNC | DELEGATED
         */
        public function test_sync_delegated()
        {

            // Skip if using SAML
            if ($this->no_sso || $this->use_saml || $this->use_b2c) {
                return;
            }

            $test_result = new Test_Result('Synchronize users with <em>delegated</em> permissions.', Test_Result::CAPABILITY_SYNC, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $suitable_extensions = \array_flip(array(
                'wpo365-customers/wpo365-customers.php',
                'wpo365-login-premium/wpo365-login.php',
                'wpo365-login-intranet/wpo365-login.php',
                'wpo365-intranet-5y/wpo365-intranet-5y.php',
                'wpo365-sync-5y/wpo365-sync-5y.php',
                'wpo365-integrate/wpo365-integrate.php',
            ));

            // Check if suitable extensions can be found
            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the SYNC feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-bundles/';
                return $test_result;
            }

            // Check if sync is en
            $sync_enabled = Options_Service::get_global_boolean_var('enable_user_sync');

            if (empty($sync_enabled)) {
                $test_result->passed = false;
                $test_result->message = "Enable User synchronization on the plugin's <a href=\"#userSync\">User sync</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'enableUserSync' => true,
                        ),
                    ),
                );
                $test_result->more_info = 'https://docs.wpo365.com/article/57-synchronize-users-from-azure-ad-to-wordpress';
                return $test_result;
            }

            // Check if cron has been disabled
            $disabled = Options_Service::get_global_boolean_var('prevent_send_email_change_email');

            if (!$disabled) {
                $test_result->passed = false;
                $test_result->message = 'It is recommended to disable the automatic email response to users when their password or email is changed during User synchronization on the plugin\'s <a href="#miscellaneous">Miscellaneous</a> configuration page.';
                $test_result->more_info = 'https://docs.wpo365.com/article/115-prevent-wordpress-to-send-email-changed-email';
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'preventSendEmailChangeEmail' => true,
                        ),
                    ),
                );
                return $test_result;
            }

            $allowed_urls = Options_Service::get_global_list_var('pages_blacklist');
            $found = false;

            foreach ($allowed_urls as $url) {

                if (false !== WordPress_Helpers::stripos($url, '/wp-json/wpo365/v1/')) {
                    $found = true;
                    break;
                }
            }

            if (empty($found)) {
                $test_result->passed = false;
                $test_result->message = 'You must add "/wp-json/wpo365/v1/" to the list of pages freed from authentication on the plugin\'s <a href="#singleSignOn">Single Sign-on</a> configuration page to be able to synchronize users.';
                $test_result->more_info = 'https://docs.wpo365.com/article/37-pages-blacklist';
                $test_result->fix = array(
                    array(
                        'op' => 'add',
                        'value' => array(
                            'pagesBlacklist' => '/wp-json/wpo365/v1/',
                        ),
                    ),
                );
                return $test_result;
            }

            // Check if suitable access token with delegated permissions can be found
            if (!$this->delegated_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->delegated_access_token_test_result->message;
                $test_result->more_info = $this->delegated_access_token_test_result->more_info;
                return $test_result;
            }

            // Check if access token has appropriate permissions
            $test_result_user_read_all = $this->check_static_permission(
                $this->delegated_access_token,
                'user.read.all',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_SYNC,
                'and as a result the plugin cannot retrieve users from your organization from Microsoft Graph to synchronize with WordPress.',
                'https://docs.wpo365.com/article/57-synchronize-users-from-azure-ad-to-wordpress'
            );

            if (!$test_result_user_read_all->passed) {
                $test_result->passed = $test_result_user_read_all->passed;
                $test_result->message = $test_result_user_read_all->message;
                $test_result->more_info = $test_result_user_read_all->more_info;
                return $test_result;
            }

            // Check if the default sync query can be executed against Microsoft Graph
            if (\class_exists('\Wpo\Sync\SyncV2_Service')) {

                $test_result->data = Graph_Service::fetch('/myorganization/users?$filter=accountEnabled+eq+true+and+userType+eq+%27member%27&$top=1', 'GET', false, array('Accept: application/json;odata.metadata=minimal'), true, false, '', 'https://graph.microsoft.com/User.Read.All');

                if (\is_wp_error($test_result->data) || !isset($test_result->data['response_code']) || $test_result->data['response_code'] != 200) {
                    $test_result->passed = false;
                    $test_result->message = 'Failed to retrieve users from your organization from Microsoft to synchronize with WordPress. Inspect the data that was received below for a possible reason.';
                    $test_result->more_info = 'https://docs.wpo365.com/article/57-synchronize-users-from-azure-ad-to-wordpress';
                }
            } else {
                $test_result->passed = false;
                $test_result->message = 'Unknow error occurred [class could not be loaded].';
                $test_result->more_info = 'https://docs.wpo365.com/article/57-synchronize-users-from-azure-ad-to-wordpress';
            }

            return $test_result;
        }

        /**
         * SYNC | APPLICATION
         */
        public function test_sync_application()
        {

            $test_result = new Test_Result('Synchronize users with <em>application</em> permissions (required when you want to schedule user synchronization).', Test_Result::CAPABILITY_SYNC, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $suitable_extensions = \array_flip(array(
                'wpo365-customers/wpo365-customers.php',
                'wpo365-login-premium/wpo365-login.php',
                'wpo365-login-intranet/wpo365-login.php',
                'wpo365-intranet-5y/wpo365-intranet-5y.php',
                'wpo365-sync-5y/wpo365-sync-5y.php',
                'wpo365-integrate/wpo365-integrate.php',
            ));

            // Check if suitable extensions can be found
            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the SYNC feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-bundles/';
                return $test_result;
            }

            // Check if sync is en
            $sync_enabled = Options_Service::get_global_boolean_var('enable_user_sync');

            if (empty($sync_enabled)) {
                $test_result->passed = false;
                $test_result->message = "Enable User synchronization on the plugin's <a href=\"#userSync\">User sync</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'enableUserSync' => true,
                        ),
                    ),
                );
                $test_result->more_info = 'https://docs.wpo365.com/article/57-synchronize-users-from-azure-ad-to-wordpress';
                return $test_result;
            }

            // Check if cron has been disabled
            $disabled = Options_Service::get_global_boolean_var('prevent_send_email_change_email');

            if (!$disabled) {
                $test_result->passed = false;
                $test_result->message = 'It is recommended to disable the automatic email response to users when their password or email is changed during User synchronization on the plugin\'s <a href="#miscellaneous">Miscellaneous</a> configuration page.';
                $test_result->more_info = 'https://docs.wpo365.com/article/115-prevent-wordpress-to-send-email-changed-email';
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'preventSendEmailChangeEmail' => true,
                        ),
                    ),
                );
                return $test_result;
            }

            $allowed_urls = Options_Service::get_global_list_var('pages_blacklist');
            $found = false;

            foreach ($allowed_urls as $url) {

                if (false !== WordPress_Helpers::stripos($url, '/wp-json/wpo365/v1/')) {
                    $found = true;
                    break;
                }
            }

            if (empty($found)) {
                $test_result->passed = false;
                $test_result->message = 'You must add "/wp-json/wpo365/v1/" to the list of pages freed from authentication on the plugin\'s <a href="#singleSignOn">Single Sign-on</a> configuration page to be able to synchronize users.';
                $test_result->more_info = 'https://docs.wpo365.com/article/37-pages-blacklist';
                $test_result->fix = array(
                    array(
                        'op' => 'add',
                        'value' => array(
                            'pagesBlacklist' => '/wp-json/wpo365/v1/',
                        ),
                    ),
                );
                return $test_result;
            }

            // Check if suitable access token with application permissions can be found
            if (!$this->application_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->application_access_token_test_result->message;
                $test_result->more_info = $this->application_access_token_test_result->more_info;
                return $test_result;
            }

            // Check if access token has appropriate permissions
            $test_result_user_read_all = $this->check_static_permission(
                $this->application_access_token,
                'user.read.all',
                'application',
                $this->application_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_SYNC,
                'and as a result the plugin cannot retrieve users from your organization from Microsoft Graph to synchronize with WordPress.',
                'https://docs.wpo365.com/article/57-synchronize-users-from-azure-ad-to-wordpress'
            );

            if (!$test_result_user_read_all->passed) {
                $test_result->passed = $test_result_user_read_all->passed;
                $test_result->message = $test_result_user_read_all->message;
                $test_result->more_info = $test_result_user_read_all->more_info;
                return $test_result;
            }

            // Check if the default sync query can be executed against Microsoft Graph
            if (\class_exists('\Wpo\Sync\SyncV2_Service')) {

                $test_result->data = Graph_Service::fetch('/myorganization/users?$filter=accountEnabled+eq+true+and+userType+eq+%27member%27&$top=1', 'GET', false, array('Accept: application/json;odata.metadata=minimal'), false, false, '', 'https://graph.microsoft.com/User.Read.All');

                if (\is_wp_error($test_result->data) || !isset($test_result->data['response_code']) || $test_result->data['response_code'] != 200) {
                    $test_result->passed = false;
                    $test_result->message = 'Failed to retrieve users from your organization from Microsoft to synchronize with WordPress. Inspect the data that was received below for a possible reason.';
                    $test_result->more_info = 'https://docs.wpo365.com/article/57-synchronize-users-from-azure-ad-to-wordpress';
                }
            } else {
                $test_result->passed = false;
                $test_result->message = 'Unknow error occurred [class could not be loaded].';
                $test_result->more_info = 'https://docs.wpo365.com/article/57-synchronize-users-from-azure-ad-to-wordpress';
            }

            return $test_result;
        }

        /**
         * SCIM | APPLICATION
         */
        public function test_scim_application()
        {
            $test_result = new Test_Result('Provision Azure AD users with <em>application</em> permissions (using the built-in SCIM client).', Test_Result::CAPABILITY_SCIM, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $suitable_extensions = \array_flip(array(
                'wpo365-login-intranet/wpo365-login.php',
                'wpo365-scim/wpo365-scim.php',
                'wpo365-intranet-5y/wpo365-intranet-5y.php',
                'wpo365-integrate/wpo365-integrate.php',
            ));

            // Check if suitable extensions can be found
            if (sizeof($this->extensions) === 0 || sizeof(\array_intersect_key($suitable_extensions, $this->extensions)) === 0) {
                $test_result->passed = false;
                $test_result->message = 'No WPO365 extension was found that would enable the SCIM feature.';
                $test_result->more_info = 'https://www.wpo365.com/compare-all-wpo365-bundles/';
                return $test_result;
            }

            // Check if scim is enabled
            $scim_enabled = Options_Service::get_global_boolean_var('enable_scim');

            if (empty($scim_enabled)) {
                $test_result->passed = false;
                $test_result->message = "Enable SCIM based Azure AD User provisioning on the plugin's <a href=\"#userSync\">User sync</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'enableScim' => true,
                        ),
                    ),
                );
                $test_result->more_info = 'https://docs.wpo365.com/article/59-wordpress-user-provisioning-with-azure-ad-scim';
                return $test_result;
            }

            // Check if SCIM secret has been defined
            if (empty(Options_Service::get_global_string_var('scim_secret_token')) && (!defined('WPO_SCIM_TOKEN') || empty(constant('WPO_SCIM_TOKEN')))) {
                $test_result->passed = false;
                $test_result->message = "As part of the Admin Credentials (for Azure AD User provisioning) you must create a secret token and add it to wp-config.php.";
                $test_result->more_info = 'https://docs.wpo365.com/article/59-wordpress-user-provisioning-with-azure-ad-scim';
                return $test_result;
            }

            $allowed_urls = Options_Service::get_global_list_var('pages_blacklist');
            $found = false;

            foreach ($allowed_urls as $url) {

                if (false !== WordPress_Helpers::stripos($url, '/wp-json/wpo365/v1/')) {
                    $found = true;
                    break;
                }
            }

            if (empty($found)) {
                $test_result->passed = false;
                $test_result->message = 'You must add "/wp-json/wpo365/v1/" to the list of pages freed from authentication on the plugin\'s <a href="#singleSignOn">Single Sign-on</a> configuration page or else Azure AD\'s User provisioning cannot connect with your website.';
                $test_result->more_info = 'https://docs.wpo365.com/article/37-pages-blacklist';
                $test_result->fix = array(
                    array(
                        'op' => 'add',
                        'value' => array(
                            'pagesBlacklist' => '/wp-json/wpo365/v1/',
                        ),
                    ),
                );
                return $test_result;
            }

            // Check if sending email changed emails has been disabled
            $disabled = Options_Service::get_global_boolean_var('prevent_send_email_change_email');

            if (!$disabled) {
                $test_result->passed = false;
                $test_result->message = 'It is recommended to disable the automatic email response to users when their password or email is changed during User synchronization on the plugin\'s <a href="#miscellaneous">Miscellaneous</a> configuration page.';
                $test_result->more_info = 'https://docs.wpo365.com/article/115-prevent-wordpress-to-send-email-changed-email';
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'preventSendEmailChangeEmail' => true,
                        ),
                    ),
                );
                return $test_result;
            }

            return $test_result;
        }

        /**
         * CONTENT BY SEARCH | DELEGATED
         */
        public function test_sharepoint_search_delegated()
        {
            // Skip if using SAML
            if ($this->no_sso || $this->use_saml || $this->use_b2c) {
                return;
            }

            $test_result = new Test_Result('Search files in SharePoint Online (as a user who signed in with Microsoft) using the WPO365 (shortcode / Gutenberg based) client-side apps.', Test_Result::CAPABILITY_APPS, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            // Check if suitable access token with delegated permissions can be found
            if (!$this->delegated_access_token_test_result->passed) {
                $test_result->passed = false;
                $test_result->message = $this->delegated_access_token_test_result->message;
                $test_result->more_info = $this->delegated_access_token_test_result->more_info;
                return $test_result;
            }

            $test_result_sites_search_all = $this->check_static_permission(
                $this->delegated_access_token,
                'sites.search.all',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_APPS,
                'and as a result a user cannot search for items in SharePoint Online.',
                'https://docs.wpo365.com/article/125-sharepoint-search-for-wordpress'
            );

            if (!$test_result_sites_search_all->passed) {
                $test_result->passed = $test_result_sites_search_all->passed;
                $test_result->message = $test_result_sites_search_all->message;
                $test_result->more_info = $test_result_sites_search_all->more_info;
                return $test_result;
            }

            if (!Options_Service::get_global_boolean_var('enable_graph_api')) {
                $test_result->passed = false;
                $test_result->message = "To enable (WPO365) client-side apps to request data from your SharePoint Online instance you must check the option to <em>Enable WPO365 REST API for Microsoft Graph</a> on the plugin's <a href=\"#integration\">Integration</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'enableGraphApi' => true,
                        ),
                    ),
                );
                return $test_result;
            }

            if (!Options_Service::get_global_boolean_var('enable_graph_proxy')) {
                $test_result->passed = false;
                $test_result->message = "To enable (WPO365) client-side apps to request data from your SharePoint Online instance you must check the option to <em>Allow Microsoft Graph proxy-type requests</a> on the plugin's <a href=\"#integration\">Integration</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'enableGraphProxy' => true,
                        ),
                    ),
                );
                return $test_result;
            }

            $allowed_endpoints_and_permissions = Options_Service::get_global_list_var('graph_allowed_endpoints');
            $sharepoint_endpoint_ok = false;

            foreach ($allowed_endpoints_and_permissions as $allowed_endpoint_config) {

                if (WordPress_Helpers::stripos($allowed_endpoint_config['key'], 'sharepoint.com') > 0 && false === $allowed_endpoint_config['boolVal']) {
                    $sharepoint_endpoint_ok = true;
                    continue;
                }
            }

            if (!$sharepoint_endpoint_ok) {
                $test_result->passed = false;
                $test_result->message = 'You must add the SharePoint Online Home URL e.g. https://your-tenant.sharepoint.com to the list of <em>Allowed endpoints</em> on the plugin\'s <a href="#integration">Integration</a> configuration page.';

                // Check if access token has appropriate permissions
                $test_result_sites_read_all = $this->check_static_permission(
                    $this->delegated_access_token,
                    'sites.read.all',
                    'delegated',
                    $this->delegated_static_permissions,
                    Test_Result::SEVERITY_LOW,
                    Test_Result::CAPABILITY_APPS,
                    'and as a result a user cannot get files from SharePoint / OneDrive.',
                    'https://tutorials.wpo365.com/courses/embed-a-sharepoint-library-in-wordpress/'
                );

                if ($test_result_sites_read_all->passed) {
                    // Check if the SharePoint home site can be retrieved                
                    $response = Graph_Service::fetch('/sites/root', 'GET', false, array('Accept: application/json;odata.metadata=minimal'), true, false, '', 'https://graph.microsoft.com/Sites.Read.All');

                    if (!\is_wp_error($response) && isset($response['response_code']) && $response['response_code'] == 200) {

                        try {
                            $this->hostname = $response['payload']['siteCollection']['hostname'];
                        } catch (\Exception $e) {
                        }
                    }
                }

                if (!empty($this->hostname)) {
                    $test_result->fix = array(
                        array(
                            'op' => 'add',
                            'value' => array(
                                'graphAllowedEndpoints' => array(
                                    'key' => sprintf('https://%s/', $this->hostname),
                                    'boolVal' => false,
                                ),
                            ),
                        ),
                    );
                }
            }

            return $test_result;
        }

        /**
         * DOCUMENTS | DELEGATED
         */
        public function test_sharepoint_documents_delegated()
        {
            // Skip if using SAML
            if ($this->no_sso || $this->use_saml || $this->use_b2c) {
                return;
            }

            $test_result = new Test_Result('View files in a SharePoint Online / OneDrive Library (as a user who signed in with Microsoft) using the WPO365 (shortcode / Gutenberg based) client-side apps.', Test_Result::CAPABILITY_APPS, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $test_result_sites_read_all = $this->check_static_permission(
                $this->delegated_access_token,
                'sites.read.all',
                'delegated',
                $this->delegated_static_permissions,
                Test_Result::SEVERITY_LOW,
                Test_Result::CAPABILITY_APPS,
                'and as a result a user cannot view in a SharePoint Online / OneDrive document library.',
                'https://tutorials.wpo365.com/courses/embed-a-sharepoint-library-in-wordpress/'
            );

            if (!$test_result_sites_read_all->passed) {
                $test_result->passed = $test_result_sites_read_all->passed;
                $test_result->message = $test_result_sites_read_all->message;
                $test_result->more_info = $test_result_sites_read_all->more_info;
                return $test_result;
            }

            if (!Options_Service::get_global_boolean_var('enable_graph_api')) {
                $test_result->passed = false;
                $test_result->message = "To enable (WPO365) client-side apps to request data from your SharePoint Online instance you must check the option to <em>Enable WPO365 REST API for Microsoft Graph</a> on the plugin's <a href=\"#integration\">Integration</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'enableGraphApi' => true,
                        ),
                    ),
                );
                return $test_result;
            }

            if (!Options_Service::get_global_boolean_var('enable_graph_proxy')) {
                $test_result->passed = false;
                $test_result->message = "To enable (WPO365) client-side apps to request data from your SharePoint Online instance you must check the option to <em>Allow Microsoft Graph proxy-type requests</a> on the plugin's <a href=\"#integration\">Integration</a> configuration page.";
                $test_result->fix = array(
                    array(
                        'op' => 'replace',
                        'value' => array(
                            'enableGraphProxy' => true,
                        ),
                    ),
                );
                return $test_result;
            }

            $allowed_endpoints_and_permissions = Options_Service::get_global_list_var('graph_allowed_endpoints');
            $drives_endpoint_ok = false;
            $sites_endpoint_ok = false;

            foreach ($allowed_endpoints_and_permissions as $allowed_endpoint_config) {

                if (WordPress_Helpers::stripos($allowed_endpoint_config['key'], '/sites') > 0) {
                    $sites_endpoint_ok = true;
                    continue;
                }

                if (WordPress_Helpers::stripos($allowed_endpoint_config['key'], '/drives') > 0) {
                    $drives_endpoint_ok = true;
                    continue;
                }
            }

            if (!$sites_endpoint_ok) {
                $test_result->passed = false;
                $test_result->message = 'You must add the following entry to the list of <em>Allowed endpoints</em> on the plugin\'s <a href="#integration">Integration</a> configuration page: https://graph.microsoft.com/_/sites.';
                $test_result->fix = array(
                    array(
                        'op' => 'add',
                        'value' => array(
                            'graphAllowedEndpoints' => array(
                                'key' => sprintf('https://graph.microsoft%s/_/sites', $this->tld),
                                'boolVal' => false,
                            ),
                        ),
                    ),
                );
                return $test_result;
            }

            if (!$drives_endpoint_ok) {
                $test_result->passed = false;
                $test_result->message = 'You must add the following entry to the list of <em>Allowed endpoints</em> on the plugin\'s <a href="#integration">Integration</a> configuration page: https://graph.microsoft.com/_/drives.';
                $test_result->fix = array(
                    array(
                        'op' => 'add',
                        'value' => array(
                            'graphAllowedEndpoints' => array(
                                'key' => sprintf('https://graph.microsoft%s/_/drives', $this->tld),
                                'boolVal' => false,
                            ),
                        ),
                    ),
                );
                return $test_result;
            }

            return $test_result;
        }

        /**
         * DELEGATED PERMISSIONS
         */
        private function get_delegated_access_token()
        {
            if ($this->no_sso || $this->use_saml || $this->use_b2c) {
                return;
            }

            $test_result = new Test_Result('Retrieve an access token with <em>delegated</em> access', Test_Result::CAPABILITY_ACCESS_TOKENS, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            $application_secret = Options_Service::get_aad_option('application_secret');

            if (empty($application_secret)) {
                $test_result->passed = false;
                $test_result->message = "An <em>Application (Client) Secret</em> for <em>Delegated access</em> has not been configured (on the <a href=\"#integration\">Integration</a> tab). Please consult the online documentation using the link below and configure the <em>Integration</em> portion of the plugin.";
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                $this->delegated_access_token_test_result = $test_result;
                return;
            }

            if (preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $application_secret)) {
                $test_result->passed = false;
                $test_result->message = 'Application (client) secret for <em>Delegated access</em> appears to be invalid. Possibly the secret\'s ID instead of its value has been copied from the corresonding page in Azure Portal.';
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                $this->application_access_token_test_result = $test_result;
                return;
            }

            $this->delegated_access_token = Access_Token_Service::get_access_token('openid profile offline_access user.read');

            if (is_wp_error($this->delegated_access_token) || !property_exists($this->delegated_access_token, 'access_token')) {
                $test_result->passed = false;
                $test_result->message = 'Could not fetch access token. The following error occurred: ' . $this->delegated_access_token->get_error_message();
                $test_result->more_info = '';
            } elseif (property_exists($this->delegated_access_token, 'scope')) {
                $this->delegated_static_permissions = explode(' ', $this->delegated_access_token->scope);
                $this->delegated_static_permissions = array_map(
                    function ($item) {
                        $lowered = strtolower($item);
                        $endpoint = sprintf('https://graph.microsoft%s/', $this->tld);
                        return str_replace($endpoint, '', $lowered);
                    },
                    $this->delegated_static_permissions
                );
            }

            $this->delegated_access_token_test_result = $test_result;
        }

        public function test_end()
        {

            if (empty($this->wpo_usr)) {
                return;
            }

            $request_service = Request_Service::get_instance();
            $this->request = $request_service->get_request($GLOBALS['WPO_CONFIG']['request_id']);
            $this->request->set_item('wpo_usr', $this->wpo_usr);
        }

        /**
         * APPLICATION PERMISSIONS
         */
        private function get_application_access_token($scope = null, $role = null)
        {
            $scope = empty($scope) ? sprintf('https://graph.microsoft%s/.default', $this->tld) : str_replace('.com', $this->tld, $scope);
            $test_result = new Test_Result('Can fetch app-only access tokens', Test_Result::CAPABILITY_ACCESS_TOKENS, Test_Result::SEVERITY_LOW);
            $test_result->passed = true;

            if (!Options_Service::get_aad_option('use_app_only_token', true)) {
                $test_result->passed = false;
                $test_result->message = "It is recommended to configure application-level access so the plugin can connect to Microsoft Services such as Microsoft Graph without a logged-in user to enable more advanced scenarios e.g. User Synchronization.";
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                $this->application_access_token_test_result = $test_result;
                return;
            }

            $application_id = Options_Service::get_aad_option('app_only_application_id');

            if (empty($application_id)) {
                $test_result->passed = false;
                $test_result->message = "It is recommended to configure application-level access so the plugin can connect to Microsoft Services such as Microsoft Graph without a logged-in user to enable more advanced scenarios e.g. User Synchronization.";
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                $this->application_access_token_test_result = $test_result;
                return;
            }

            if (!preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/', $application_id)) {
                $test_result->passed = false;
                $test_result->message = 'Support for application-level (app-only) access to Microsoft Services has been enabled but the app-only Application (client) ID on the plugin\'s <a href="#integration">Integration</a> page is not a valid GUID';
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                $this->application_access_token_test_result = $test_result;
                return;
            }

            $application_secret = Options_Service::get_aad_option('app_only_application_secret');

            if (empty($application_secret)) {
                $test_result->passed = false;
                $test_result->message = "The use of application permissions is enabled. However, the plugin failed to retrieve the necessary application (client) secret for the corresponding (Azure AD) <em>registered application</em> with ID <strong>$application_id</strong>. Please create a Client secret for the (Azure AD) <em>registered application</em> in Azure AD and enter it on the plugin's <a href=\"#integration\">Integration</a>.";
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                $this->application_access_token_test_result = $test_result;
                return;
            }

            if (preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $application_secret)) {
                $test_result->passed = false;
                $test_result->message = 'App-only Application (client) secret for (Azure AD) <em>registered application</em> with ID <strong>' . $application_id . '</strong> appears to be invalid. Possibly the secret\'s ID instead of its value has been copied from the corresonding page for the (Azure AD) <em>registered application</em> in Azure Portal.';
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                $this->application_access_token_test_result = $test_result;
                return;
            }

            $this->application_access_token = Access_Token_Service::get_app_only_access_token($scope, $role);

            if (is_wp_error($this->application_access_token) || !property_exists($this->application_access_token, 'access_token')) {
                $test_result->passed = false;
                $test_result->message = 'Could not fetch app-only access token from the (Azure AD) <em>registered application</em> with ID <strong>' . $application_id . '</strong>. The following error occurred: ' . $this->application_access_token->get_error_message();
                $test_result->more_info = '';
                $this->application_access_token_test_result = $test_result;
                return;
            }

            if (empty($this->application_access_token->roles)) {
                $test_result->passed = false;
                $test_result->message = 'Support for application-level (app-only) access to Microsoft Services has been configured for (Azure AD) <em>registered application</em> with ID <strong>' . $application_id . '</strong>. However, the access token that was retrieved does not have any roles (= application-level API permissions) assigned / granted.';
                $test_result->more_info = 'https://docs.wpo365.com/article/23-integration';
                $this->application_access_token_test_result = $test_result;
                return;
            }

            $this->application_static_permissions = $this->application_access_token->roles;
            $this->application_access_token_test_result = $test_result;
        }

        private function check_static_permission($access_token, $permission, $permission_type, $static_permissions, $severity, $category = Test_Result::CAPABILITY_CONFIG, $additional_message = '', $more_info = 'https://docs.wpo365.com/article/23-integration')
        {
            $endpoint = sprintf('https://graph.microsoft%s/', $this->tld);
            $permission = str_replace($endpoint, '', $permission);

            if (strcasecmp($permission, 'mail.send') === 0) {
                $application_id = Options_Service::get_mail_option('mail_application_id');
            } elseif (strcasecmp($permission_type, 'delegated') === 0) {
                $application_id = Options_Service::get_aad_option('application_id');
            } else {
                $application_id = Options_Service::get_aad_option('app_only_application_id');
            }

            $test_result = new Test_Result("Static <strong>$permission_type</strong> permission <strong>$permission</strong> has been configured for the (Azure AD) <em>registered application</em> with ID <strong>$application_id</strong>", $category, $severity);
            $test_result->passed = true;

            if (empty($access_token)) {
                $test_result->passed = false;
                $test_result->message = "Could not fetch $permission_type access token -> test skipped";
                $test_result->more_info = '';
                return $test_result;
            }

            if (empty($static_permissions)) {
                $test_result->passed = false;
                $test_result->message = "Could not determine static permissions of the current $permission_type access token -> test skipped";
                $test_result->more_info = '';
                return $test_result;
            }

            foreach ($static_permissions as $key => $static_permission) {

                if (false !== WordPress_Helpers::stripos($static_permission, $permission)) {
                    return $test_result;
                }
            }

            $test_result->passed = false;
            $test_result->message = "Static <strong>$permission_type</strong> permission <strong>$permission</strong> is not configured for the (Azure AD) <em>registered application</em> with ID <strong>$application_id</strong> $additional_message";
            $test_result->more_info = $more_info;
            return $test_result;
        }
    }
}

Youez - 2016 - github.com/yon3zu
LinuXploit