mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
Merge branch 'MDL-66776-master' of https://github.com/jleyva/moodle
This commit is contained in:
commit
97764798a4
@ -1264,6 +1264,7 @@ $string['messageprovider:errors'] = 'Important errors with the site';
|
||||
$string['messageprovider:errors_help'] = 'These are important errors that an administrator should know about.';
|
||||
$string['messageprovider:gradenotifications'] = 'Grade notifications';
|
||||
$string['messageprovider:messagecontactrequests'] = 'Message contact requests notification';
|
||||
$string['messageprovider:newlogin'] = 'New login notifications';
|
||||
$string['messageprovider:notices'] = 'Notices about minor problems';
|
||||
$string['messageprovider:notices_help'] = 'These are notices that an administrator might be interested in seeing.';
|
||||
$string['messageprovider:infected'] = 'Antivirus failure notifications.';
|
||||
@ -1386,6 +1387,18 @@ $string['new'] = 'New';
|
||||
$string['newaccount'] = 'New account';
|
||||
$string['newactivityname'] = 'New name for activity {$a}';
|
||||
$string['newcourse'] = 'New course';
|
||||
$string['newloginnotificationtitle'] = 'New sign in to your {$a} account';
|
||||
$string['newloginnotificationbodysmall'] = 'Your {$a} account was just signed in to from a new device.';
|
||||
$string['newloginnotificationbodyfull'] = '<p>Hi {$a->userfullname},</p>
|
||||
<p>Your {$a->sitename} account was just signed in to from a new device.</p>
|
||||
<ul>
|
||||
<li>Your account: {$a->username} {$a->useremail}</li>
|
||||
<li>{$a->logintime}</li>
|
||||
<li>Device: {$a->logindevice}</li>
|
||||
<li>IP: {$a->loginip}</li>
|
||||
</ul>
|
||||
<p>If this was you, then you don\'t need to do anything.</p>
|
||||
<p>If you don\'t recognise this activity, please <a href="{$a->changepasswordlink}">change your password</a></p>';
|
||||
$string['newpassword'] = 'New password';
|
||||
$string['newpassword_help'] = 'Enter a new password or leave blank to keep current password.';
|
||||
$string['newpasswordfromlost'] = '<strong>NOTICE:</strong> Your <strong>Current password</strong> will have been sent to you in the <strong>second</strong> of the two emails sent as part of the lost password recovery process. Make sure you have received your replacement password before continuing with this screen.';
|
||||
|
@ -274,8 +274,10 @@ class manager {
|
||||
* This is intended for installation scripts, unit tests and other
|
||||
* special areas. Do NOT use for logout and session termination
|
||||
* in normal requests!
|
||||
*
|
||||
* @param mixed $newsid only used after initialising a user session, is this a new user session?
|
||||
*/
|
||||
public static function init_empty_session() {
|
||||
public static function init_empty_session(?bool $newsid = null) {
|
||||
global $CFG;
|
||||
|
||||
if (isset($GLOBALS['SESSION']->notifications)) {
|
||||
@ -283,6 +285,9 @@ class manager {
|
||||
$notifications = $GLOBALS['SESSION']->notifications;
|
||||
}
|
||||
$GLOBALS['SESSION'] = new \stdClass();
|
||||
if (isset($newsid)) {
|
||||
$GLOBALS['SESSION']->isnewsessioncookie = $newsid;
|
||||
}
|
||||
|
||||
$GLOBALS['USER'] = new \stdClass();
|
||||
$GLOBALS['USER']->id = 0;
|
||||
@ -419,7 +424,7 @@ class manager {
|
||||
if (!$sid) {
|
||||
// No session, very weird.
|
||||
error_log('Missing session ID, session not started!');
|
||||
self::init_empty_session();
|
||||
self::init_empty_session($newsid);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -547,7 +552,7 @@ class manager {
|
||||
self::set_user($user);
|
||||
self::add_session_record($user->id);
|
||||
} else {
|
||||
self::init_empty_session();
|
||||
self::init_empty_session($newsid);
|
||||
self::add_session_record(0);
|
||||
}
|
||||
|
||||
|
96
lib/classes/task/send_login_notifications.php
Normal file
96
lib/classes/task/send_login_notifications.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Adhoc task that send login notifications.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 Moodle Pty Ltd.
|
||||
* @author Juan Leyva <juan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class send_login_notifications extends adhoc_task {
|
||||
|
||||
use \core\task\logging_trait;
|
||||
|
||||
/**
|
||||
* Run the adhoc task and preform the backup.
|
||||
*/
|
||||
public function execute() {
|
||||
global $CFG, $DB, $SITE, $USER, $PAGE;
|
||||
|
||||
$customdata = $this->get_custom_data();
|
||||
|
||||
// First check the mobile app special case, to detect if the user is not using a new device after login from a different IP.
|
||||
if (!empty($customdata->ismoodleapp)) {
|
||||
$where = 'userid = ? AND timecreated >= ?';
|
||||
if (!$DB->count_records_select('user_devices', $where, [$USER->id, $customdata->logintime])) {
|
||||
// Do nothing, seems to be the same person doing login from a new IP using a known device.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->log_start("Sending login notification to {$USER->username}");
|
||||
$sitename = format_string($SITE->fullname);
|
||||
$siteurl = $CFG->wwwroot;
|
||||
$userfullname = fullname($USER);
|
||||
$username = $USER->username;
|
||||
$useremail = ($USER->username != $USER->email) ? $USER->email : '';
|
||||
$logindevice = $customdata->ismoodleapp ? get_string('mobileapp', 'tool_mobile') : '';
|
||||
$logindevice .= ' ' . $customdata->useragent;
|
||||
$loginip = $customdata->loginip;
|
||||
$logintime = userdate($customdata->logintime);
|
||||
|
||||
$changepasswordlink = (new \moodle_url('/user/preferences.php', ['userid' => $USER->id]))->out(false);
|
||||
// Find a better final URL for changing password.
|
||||
$userauth = get_auth_plugin($USER->auth);
|
||||
if ($userauth->can_change_password()) {
|
||||
if ($changepwurl = $userauth->change_password_url()) {
|
||||
$changepasswordlink = $changepwurl;
|
||||
} else {
|
||||
$changepasswordlink = (new \moodle_url('/login/change_password.php'))->out(false);
|
||||
}
|
||||
}
|
||||
|
||||
$eventdata = new \core\message\message();
|
||||
$eventdata->courseid = SITEID;
|
||||
$eventdata->component = 'moodle';
|
||||
$eventdata->name = 'newlogin';
|
||||
$eventdata->userfrom = \core_user::get_noreply_user();
|
||||
$eventdata->userto = $USER;
|
||||
$eventdata->notification = 1;
|
||||
$eventdata->subject = get_string('newloginnotificationtitle', 'moodle', $sitename);
|
||||
$eventdata->fullmessageformat = FORMAT_HTML;
|
||||
$info = compact('sitename', 'siteurl', 'userfullname', 'username', 'useremail',
|
||||
'logindevice', 'logintime', 'loginip', 'changepasswordlink');
|
||||
$eventdata->fullmessagehtml = get_string('newloginnotificationbodyfull', 'moodle', $info);
|
||||
$eventdata->fullmessage = html_to_text($eventdata->fullmessagehtml);
|
||||
$eventdata->smallmessage = get_string('newloginnotificationbodysmall', 'moodle', $username);
|
||||
|
||||
$userpicture = new \user_picture($USER);
|
||||
$userpicture->size = 1; // Use f1 size.
|
||||
$userpicture->includetoken = $USER->id; // Generate an out-of-session token for the user receiving the message.
|
||||
$eventdata->customdata = ['notificationiconurl' => $userpicture->get_url($PAGE)->out(false)];
|
||||
|
||||
if (message_send($eventdata)) {
|
||||
$this->log_finish("Notification successfully sent");
|
||||
} else {
|
||||
$this->log_finish("Failed to send notification");
|
||||
}
|
||||
}
|
||||
}
|
@ -35,6 +35,13 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$messageproviders = array (
|
||||
|
||||
'newlogin' => array (
|
||||
'defaults' => array(
|
||||
'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF,
|
||||
'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF,
|
||||
),
|
||||
),
|
||||
|
||||
// Notices that an admin might be interested in
|
||||
'notices' => array (
|
||||
'capability' => 'moodle/site:config'
|
||||
|
@ -1172,7 +1172,7 @@ function external_generate_token_for_current_user($service) {
|
||||
* @since Moodle 3.2
|
||||
*/
|
||||
function external_log_token_request($token) {
|
||||
global $DB;
|
||||
global $DB, $USER;
|
||||
|
||||
$token->privatetoken = null;
|
||||
|
||||
@ -1185,6 +1185,25 @@ function external_log_token_request($token) {
|
||||
$event = \core\event\webservice_token_sent::create($params);
|
||||
$event->add_record_snapshot('external_tokens', $token);
|
||||
$event->trigger();
|
||||
|
||||
// Check if we need to notify the user about the new login via token.
|
||||
$loginip = getremoteaddr();
|
||||
if ($USER->lastip != $loginip &&
|
||||
((!WS_SERVER && !CLI_SCRIPT && NO_MOODLE_COOKIES) || PHPUNIT_TEST)) {
|
||||
|
||||
$logintime = time();
|
||||
$useragent = \core_useragent::get_user_agent_string();
|
||||
$ismoodleapp = \core_useragent::is_moodle_app();
|
||||
|
||||
// Schedule adhoc task to sent a login notification to the user.
|
||||
$task = new \core\task\send_login_notifications();
|
||||
$task->set_userid($USER->id);
|
||||
$task->set_custom_data(compact('ismoodleapp', 'useragent', 'loginip', 'logintime'));
|
||||
$task->set_component('core');
|
||||
// We need sometime so the mobile app will send to Moodle the device information after login.
|
||||
$task->set_next_run_time($logintime + (2 * MINSECS));
|
||||
\core\task\manager::reschedule_or_queue_adhoc_task($task);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3351,7 +3351,7 @@ function get_user_key($script, $userid, $instance=null, $iprestriction=null, $va
|
||||
* @return bool Always returns true
|
||||
*/
|
||||
function update_user_login_times() {
|
||||
global $USER, $DB;
|
||||
global $USER, $DB, $SESSION;
|
||||
|
||||
if (isguestuser()) {
|
||||
// Do not update guest access times/ips for performance.
|
||||
@ -3375,6 +3375,7 @@ function update_user_login_times() {
|
||||
|
||||
// Function user_accesstime_log() may not update immediately, better do it here.
|
||||
$USER->lastaccess = $user->lastaccess = $now;
|
||||
$SESSION->userpreviousip = $USER->lastip;
|
||||
$USER->lastip = $user->lastip = getremoteaddr();
|
||||
|
||||
// Note: do not call user_update_user() here because this is part of the login process,
|
||||
@ -4545,6 +4546,27 @@ function complete_user_login($user) {
|
||||
);
|
||||
$event->trigger();
|
||||
|
||||
// Check if the user is using a new browser or session (a new MoodleSession cookie is set in that case).
|
||||
// If the user is accessing from the same IP, ignore everything (most of the time will be a new session in the same browser).
|
||||
// Skip Web Service requests, CLI scripts, AJAX scripts, and request from the mobile app itself.
|
||||
$loginip = getremoteaddr();
|
||||
$isnewip = isset($SESSION->userpreviousip) && $SESSION->userpreviousip != $loginip;
|
||||
$isvalidenv = (!WS_SERVER && !CLI_SCRIPT && !NO_MOODLE_COOKIES) || PHPUNIT_TEST;
|
||||
|
||||
if (!empty($SESSION->isnewsessioncookie) && $isnewip && $isvalidenv && !\core_useragent::is_moodle_app()) {
|
||||
|
||||
$logintime = time();
|
||||
$ismoodleapp = false;
|
||||
$useragent = \core_useragent::get_user_agent_string();
|
||||
|
||||
// Schedule adhoc task to sent a login notification to the user.
|
||||
$task = new \core\task\send_login_notifications();
|
||||
$task->set_userid($USER->id);
|
||||
$task->set_custom_data(compact('ismoodleapp', 'useragent', 'loginip', 'logintime'));
|
||||
$task->set_component('core');
|
||||
\core\task\manager::queue_adhoc_task($task);
|
||||
}
|
||||
|
||||
// Queue migrating the messaging data, if we need to.
|
||||
if (!get_user_preferences('core_message_migrate_data', false, $USER->id)) {
|
||||
// Check if there are any legacy messages to migrate.
|
||||
|
@ -115,6 +115,7 @@ completely removed from Moodle core too.
|
||||
* Final deprecation: The following functions along with associated tests have been removed:
|
||||
- core_grades_external::get_grades
|
||||
- core_grades_external::get_grade_item
|
||||
* \core\session\manager::init_empty_session() has a new optional parameter $newsid to indicate whether this is a new user session
|
||||
|
||||
=== 3.11.4 ===
|
||||
* A new option dontforcesvgdownload has been added to the $options parameter of the send_file() function.
|
||||
|
271
login/tests/notifications_test.php
Normal file
271
login/tests/notifications_test.php
Normal file
@ -0,0 +1,271 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains tests for course related notifications.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 Juan Leyva <juan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_login_notifications_testcase extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent.
|
||||
|
||||
external_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);
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2021110600.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2021110800.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '4.0dev+ (Build: 20211106)'; // Human-friendly version name
|
||||
|
Loading…
x
Reference in New Issue
Block a user