See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core\task; 18 19 use moodle_url; 20 21 /** 22 * Contains tests for login related notifications. 23 * 24 * @package core 25 * @copyright 2021 Juan Leyva <juan@moodle.com> 26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 * @covers \core\task\send_login_notifications 28 */ 29 class send_login_notifications_test extends \advanced_testcase { 30 31 /** 32 * Load required classes 33 */ 34 public static function setUpBeforeClass(): void { 35 global $CFG; 36 37 require_once($CFG->libdir . '/externallib.php'); 38 } 39 40 /** 41 * Test new login notification. 42 */ 43 public function test_login_notification() { 44 global $SESSION; 45 46 $this->resetAfterTest(); 47 48 $loginuser = self::getDataGenerator()->create_user(); 49 $this->setUser(0); 50 51 // Mock data for test. 52 $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. 53 $SESSION->isnewsessioncookie = true; // New session cookie. 54 @complete_user_login($loginuser); 55 56 // Redirect messages to sink and stop buffer output from CLI task. 57 $sink = $this->redirectMessages(); 58 ob_start(); 59 $this->runAdhocTasks('\core\task\send_login_notifications'); 60 $output = ob_get_contents(); 61 ob_end_clean(); 62 $messages = $sink->get_messages(); 63 $sink->close(); 64 65 // Send notification, new IP and new session. 66 $this->assertCount(1, $messages); 67 $this->assertEquals($loginuser->id, $messages[0]->useridto); 68 $this->assertEquals('newlogin', $messages[0]->eventtype); 69 } 70 71 /** 72 * Test new login notification is skipped because of same IP from last login. 73 */ 74 public function test_login_notification_skip_same_ip() { 75 global $SESSION; 76 77 $this->resetAfterTest(); 78 79 $loginuser = self::getDataGenerator()->create_user(); 80 $this->setUser(0); 81 82 // Mock data for test. 83 $SESSION->isnewsessioncookie = true; // New session cookie. 84 @complete_user_login($loginuser); 85 86 // Redirect messages to sink and stop buffer output from CLI task. 87 $sink = $this->redirectMessages(); 88 ob_start(); 89 $this->runAdhocTasks('\core\task\send_login_notifications'); 90 $output = ob_get_contents(); 91 ob_end_clean(); 92 $messages = $sink->get_messages(); 93 $sink->close(); 94 95 // Skip notification when we have the same previous IP even if the browser used to connect is new. 96 $this->assertCount(0, $messages); 97 } 98 99 /** 100 * Test new login notification is skipped because of same browser from last login. 101 */ 102 public function test_login_notification_skip_same_browser() { 103 global $SESSION; 104 105 $this->resetAfterTest(); 106 107 $loginuser = self::getDataGenerator()->create_user(); 108 $this->setUser(0); 109 110 // Mock data for test. 111 $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. 112 $SESSION->isnewsessioncookie = false; 113 @complete_user_login($loginuser); 114 115 // Redirect messages to sink and stop buffer output from CLI task. 116 $sink = $this->redirectMessages(); 117 ob_start(); 118 $this->runAdhocTasks('\core\task\send_login_notifications'); 119 $output = ob_get_contents(); 120 ob_end_clean(); 121 $messages = $sink->get_messages(); 122 $sink->close(); 123 124 // Skip notification, different ip but same browser (probably, mobile phone browser). 125 $this->assertCount(0, $messages); 126 } 127 128 /** 129 * Test new login notification is skipped because of auto-login from the mobile app (skip duplicated notifications). 130 */ 131 public function test_login_notification_skip_mobileapp() { 132 global $SESSION; 133 134 $this->resetAfterTest(); 135 136 $loginuser = self::getDataGenerator()->create_user(); 137 $this->setUser(0); 138 139 // Mock data for test. 140 $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. 141 $SESSION->isnewsessioncookie = true; // New session cookie. 142 \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. 143 @complete_user_login($loginuser); 144 145 // Redirect messages to sink and stop buffer output from CLI task. 146 $sink = $this->redirectMessages(); 147 ob_start(); 148 $this->runAdhocTasks('\core\task\send_login_notifications'); 149 $output = ob_get_contents(); 150 ob_end_clean(); 151 $messages = $sink->get_messages(); 152 $sink->close(); 153 154 $this->assertCount(0, $messages); 155 } 156 157 /** 158 * Test new login notification where the user auth method provides a custom change password URL 159 */ 160 public function test_login_notification_custom_change_password_url(): void { 161 global $SESSION; 162 163 $this->resetAfterTest(); 164 $this->setUser(0); 165 166 // Set LDAP auth change password URL. 167 $changepasswordurl = (new moodle_url('/changepassword.php'))->out(false); 168 set_config('changepasswordurl', $changepasswordurl, 'auth_ldap'); 169 170 $ldapuser = $this->getDataGenerator()->create_user(['auth' => 'ldap']); 171 172 // Mock data for test. 173 $ldapuser->lastip = '1.2.3.4'; 174 $SESSION->isnewsessioncookie = true; 175 @complete_user_login($ldapuser); 176 177 // Redirect messages to sink and stop buffer output from CLI task. 178 $sink = $this->redirectMessages(); 179 ob_start(); 180 $this->runAdhocTasks(send_login_notifications::class); 181 ob_end_clean(); 182 $messages = $sink->get_messages(); 183 $sink->close(); 184 185 // Send notification, assert custom change password URL is present. 186 $this->assertCount(1, $messages); 187 $this->assertStringContainsString("If you don't recognise this activity, please " . 188 "<a href=\"{$changepasswordurl}\">change your password</a>.", $messages[0]->fullmessagehtml); 189 } 190 191 /** 192 * Test new mobile app login notification. 193 */ 194 public function test_mobile_app_login_notification() { 195 global $USER, $DB, $SESSION; 196 197 $this->resetAfterTest(); 198 199 $loginuser = self::getDataGenerator()->create_user(); 200 $this->setUser($loginuser); 201 202 // Mock data for test. 203 $USER->lastip = '1.2.3.4.6'; // Different ip that current. 204 205 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 206 $token = external_generate_token_for_current_user($service); 207 \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. 208 209 // Simulate we are using an new device. 210 $fakedevice = (object) [ 211 'userid' => $USER->id, 212 'appid' => 'com.moodle.moodlemobile', 213 'name' => 'occam', 214 'model' => 'Nexus 4', 215 'platform' => 'Android', 216 'version' => '4.2.2', 217 'pushid' => 'kishUhd', 218 'uuid' => 'KIhud7s', 219 'timecreated' => time() + MINSECS, 220 'timemodified' => time() + MINSECS 221 ]; 222 $DB->insert_record('user_devices', $fakedevice); 223 224 external_log_token_request($token); 225 226 // Redirect messages to sink and stop buffer output from CLI task. 227 $sink = $this->redirectMessages(); 228 ob_start(); 229 $this->runAdhocTasks('\core\task\send_login_notifications'); 230 $output = ob_get_contents(); 231 ob_end_clean(); 232 $messages = $sink->get_messages(); 233 $sink->close(); 234 235 // We sent a login notification because we are using a new device and different IP. 236 $this->assertCount(1, $messages); 237 $this->assertEquals($loginuser->id, $messages[0]->useridto); 238 $this->assertEquals('newlogin', $messages[0]->eventtype); 239 } 240 241 /** 242 * Test new mobile app login notification skipped becase of same last ip. 243 */ 244 public function test_mobile_app_login_notification_skip_same_ip() { 245 global $USER, $DB, $SESSION; 246 247 $this->resetAfterTest(); 248 249 $loginuser = self::getDataGenerator()->create_user(); 250 $this->setUser($loginuser); 251 252 // Mock data for test. 253 $USER->lastip = '0.0.0.0'; 254 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 255 $token = external_generate_token_for_current_user($service); 256 \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. 257 258 // Simulate we are using an new device. 259 $fakedevice = (object) [ 260 'userid' => $USER->id, 261 'appid' => 'com.moodle.moodlemobile', 262 'name' => 'occam', 263 'model' => 'Nexus 4', 264 'platform' => 'Android', 265 'version' => '4.2.2', 266 'pushid' => 'kishUhd', 267 'uuid' => 'KIhud7s', 268 'timecreated' => time() + MINSECS, 269 'timemodified' => time() + MINSECS 270 ]; 271 $DB->insert_record('user_devices', $fakedevice); 272 273 external_log_token_request($token); 274 275 // Redirect messages to sink and stop buffer output from CLI task. 276 $sink = $this->redirectMessages(); 277 ob_start(); 278 $this->runAdhocTasks('\core\task\send_login_notifications'); 279 $output = ob_get_contents(); 280 ob_end_clean(); 281 $messages = $sink->get_messages(); 282 $sink->close(); 283 284 // While using the same IP avoid sending new login notifications even if we are using a new device. 285 $this->assertCount(0, $messages); 286 } 287 288 /** 289 * Test new mobile app login notification skipped becase of same device. 290 */ 291 public function test_mobile_app_login_notification_skip_same_device() { 292 global $USER, $DB, $SESSION; 293 294 $this->resetAfterTest(); 295 296 $loginuser = self::getDataGenerator()->create_user(); 297 $this->setUser($loginuser); 298 299 // Mock data for test. 300 $USER->lastip = '1.2.3.4.6'; // New ip. 301 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 302 $token = external_generate_token_for_current_user($service); 303 \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. 304 305 external_log_token_request($token); 306 307 // Redirect messages to sink and stop buffer output from CLI task. 308 $sink = $this->redirectMessages(); 309 ob_start(); 310 $this->runAdhocTasks('\core\task\send_login_notifications'); 311 $output = ob_get_contents(); 312 ob_end_clean(); 313 $messages = $sink->get_messages(); 314 $sink->close(); 315 316 // While using the same device avoid sending new login notifications even if the IP changes. 317 $this->assertCount(0, $messages); 318 } 319 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body