See Release Notes
Long Term Support Release
<?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. namespace core\task; use moodle_url; /** * Contains tests for login related notifications. * * @package core * @copyright 2021 Juan Leyva <juan@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @covers \core\task\send_login_notifications */ class send_login_notifications_test extends \advanced_testcase { /**< * Load required classes < */ < public static function setUpBeforeClass(): void { < global $CFG; < < require_once($CFG->libdir . '/externallib.php'); < } < < /*** Test new login notification. */ public function test_login_notification() { global $SESSION; $this->resetAfterTest(); $loginuser = self::getDataGenerator()->create_user(); $this->setUser(0); // Mock data for test. $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. $SESSION->isnewsessioncookie = true; // New session cookie. @complete_user_login($loginuser); // Redirect messages to sink and stop buffer output from CLI task. $sink = $this->redirectMessages(); ob_start(); $this->runAdhocTasks('\core\task\send_login_notifications'); $output = ob_get_contents(); ob_end_clean(); $messages = $sink->get_messages(); $sink->close(); // Send notification, new IP and new session. $this->assertCount(1, $messages); $this->assertEquals($loginuser->id, $messages[0]->useridto); $this->assertEquals('newlogin', $messages[0]->eventtype); } /** * Test new login notification is skipped because of same IP from last login. */ public function test_login_notification_skip_same_ip() { global $SESSION; $this->resetAfterTest(); $loginuser = self::getDataGenerator()->create_user(); $this->setUser(0); // Mock data for test. $SESSION->isnewsessioncookie = true; // New session cookie. @complete_user_login($loginuser); // Redirect messages to sink and stop buffer output from CLI task. $sink = $this->redirectMessages(); ob_start(); $this->runAdhocTasks('\core\task\send_login_notifications'); $output = ob_get_contents(); ob_end_clean(); $messages = $sink->get_messages(); $sink->close(); // Skip notification when we have the same previous IP even if the browser used to connect is new. $this->assertCount(0, $messages); } /** * Test new login notification is skipped because of same browser from last login. */ public function test_login_notification_skip_same_browser() { global $SESSION; $this->resetAfterTest(); $loginuser = self::getDataGenerator()->create_user(); $this->setUser(0); // Mock data for test. $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. $SESSION->isnewsessioncookie = false; @complete_user_login($loginuser); // Redirect messages to sink and stop buffer output from CLI task. $sink = $this->redirectMessages(); ob_start(); $this->runAdhocTasks('\core\task\send_login_notifications'); $output = ob_get_contents(); ob_end_clean(); $messages = $sink->get_messages(); $sink->close(); // Skip notification, different ip but same browser (probably, mobile phone browser). $this->assertCount(0, $messages); } /** * Test new login notification is skipped because of auto-login from the mobile app (skip duplicated notifications). */ public function test_login_notification_skip_mobileapp() { global $SESSION; $this->resetAfterTest(); $loginuser = self::getDataGenerator()->create_user(); $this->setUser(0); // Mock data for test. $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. $SESSION->isnewsessioncookie = true; // New session cookie. \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. @complete_user_login($loginuser); // Redirect messages to sink and stop buffer output from CLI task. $sink = $this->redirectMessages(); ob_start(); $this->runAdhocTasks('\core\task\send_login_notifications'); $output = ob_get_contents(); ob_end_clean(); $messages = $sink->get_messages(); $sink->close(); $this->assertCount(0, $messages); } /** * Test new login notification where the user auth method provides a custom change password URL */ public function test_login_notification_custom_change_password_url(): void { global $SESSION; $this->resetAfterTest(); $this->setUser(0); // Set LDAP auth change password URL. $changepasswordurl = (new moodle_url('/changepassword.php'))->out(false); set_config('changepasswordurl', $changepasswordurl, 'auth_ldap'); $ldapuser = $this->getDataGenerator()->create_user(['auth' => 'ldap']); // Mock data for test. $ldapuser->lastip = '1.2.3.4'; $SESSION->isnewsessioncookie = true; @complete_user_login($ldapuser); // Redirect messages to sink and stop buffer output from CLI task. $sink = $this->redirectMessages(); ob_start(); $this->runAdhocTasks(send_login_notifications::class); ob_end_clean(); $messages = $sink->get_messages(); $sink->close(); // Send notification, assert custom change password URL is present. $this->assertCount(1, $messages); $this->assertStringContainsString("If you don't recognise this activity, please " . "<a href=\"{$changepasswordurl}\">change your password</a>.", $messages[0]->fullmessagehtml); } /** * Test new mobile app login notification. */ public function test_mobile_app_login_notification() { global $USER, $DB, $SESSION; $this->resetAfterTest(); $loginuser = self::getDataGenerator()->create_user(); $this->setUser($loginuser); // Mock data for test. $USER->lastip = '1.2.3.4.6'; // Different ip that current. $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));< $token = external_generate_token_for_current_user($service);> $token = \core_external\util::generate_token_for_current_user($service);\core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. // Simulate we are using an new device. $fakedevice = (object) [ 'userid' => $USER->id, 'appid' => 'com.moodle.moodlemobile', 'name' => 'occam', 'model' => 'Nexus 4', 'platform' => 'Android', 'version' => '4.2.2', 'pushid' => 'kishUhd', 'uuid' => 'KIhud7s', 'timecreated' => time() + MINSECS, 'timemodified' => time() + MINSECS ]; $DB->insert_record('user_devices', $fakedevice);< external_log_token_request($token);> \core_external\util::log_token_request($token);// Redirect messages to sink and stop buffer output from CLI task. $sink = $this->redirectMessages(); ob_start(); $this->runAdhocTasks('\core\task\send_login_notifications'); $output = ob_get_contents(); ob_end_clean(); $messages = $sink->get_messages(); $sink->close(); // We sent a login notification because we are using a new device and different IP. $this->assertCount(1, $messages); $this->assertEquals($loginuser->id, $messages[0]->useridto); $this->assertEquals('newlogin', $messages[0]->eventtype); } /** * Test new mobile app login notification skipped becase of same last ip. */ public function test_mobile_app_login_notification_skip_same_ip() { global $USER, $DB, $SESSION; $this->resetAfterTest(); $loginuser = self::getDataGenerator()->create_user(); $this->setUser($loginuser); // Mock data for test. $USER->lastip = '0.0.0.0'; $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));< $token = external_generate_token_for_current_user($service);> $token = \core_external\util::generate_token_for_current_user($service);\core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. // Simulate we are using an new device. $fakedevice = (object) [ 'userid' => $USER->id, 'appid' => 'com.moodle.moodlemobile', 'name' => 'occam', 'model' => 'Nexus 4', 'platform' => 'Android', 'version' => '4.2.2', 'pushid' => 'kishUhd', 'uuid' => 'KIhud7s', 'timecreated' => time() + MINSECS, 'timemodified' => time() + MINSECS ]; $DB->insert_record('user_devices', $fakedevice);< external_log_token_request($token);> \core_external\util::log_token_request($token);// Redirect messages to sink and stop buffer output from CLI task. $sink = $this->redirectMessages(); ob_start(); $this->runAdhocTasks('\core\task\send_login_notifications'); $output = ob_get_contents(); ob_end_clean(); $messages = $sink->get_messages(); $sink->close(); // While using the same IP avoid sending new login notifications even if we are using a new device. $this->assertCount(0, $messages); } /** * Test new mobile app login notification skipped becase of same device. */ public function test_mobile_app_login_notification_skip_same_device() { global $USER, $DB, $SESSION; $this->resetAfterTest(); $loginuser = self::getDataGenerator()->create_user(); $this->setUser($loginuser); // Mock data for test. $USER->lastip = '1.2.3.4.6'; // New ip. $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));< $token = external_generate_token_for_current_user($service);> $token = \core_external\util::generate_token_for_current_user($service);\core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent.< external_log_token_request($token);> \core_external\util::log_token_request($token);// Redirect messages to sink and stop buffer output from CLI task. $sink = $this->redirectMessages(); ob_start(); $this->runAdhocTasks('\core\task\send_login_notifications'); $output = ob_get_contents(); ob_end_clean(); $messages = $sink->get_messages(); $sink->close(); // While using the same device avoid sending new login notifications even if the IP changes. $this->assertCount(0, $messages); } }