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; 18 19 defined('MOODLE_INTERNAL') || die(); 20 21 global $CFG; 22 require_once($CFG->dirroot . '/lib/externallib.php'); 23 24 /** 25 * Contains tests for course related notifications. 26 * 27 * @package core 28 * @copyright 2021 Juan Leyva <juan@moodle.com> 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 */ 31 class login_notifications_test extends \advanced_testcase { 32 33 /** 34 * Load required classes 35 */ 36 public static function setUpBeforeClass(): void { 37 global $CFG; 38 39 require_once($CFG->libdir . '/externallib.php'); 40 } 41 42 /** 43 * Test new login notification. 44 */ 45 public function test_login_notification() { 46 global $SESSION; 47 48 $this->resetAfterTest(); 49 50 $loginuser = self::getDataGenerator()->create_user(); 51 $this->setUser(0); 52 53 // Mock data for test. 54 $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. 55 $SESSION->isnewsessioncookie = true; // New session cookie. 56 @complete_user_login($loginuser); 57 58 // Redirect messages to sink and stop buffer output from CLI task. 59 $sink = $this->redirectMessages(); 60 ob_start(); 61 $this->runAdhocTasks('\core\task\send_login_notifications'); 62 $output = ob_get_contents(); 63 ob_end_clean(); 64 $messages = $sink->get_messages(); 65 $sink->close(); 66 67 // Send notification, new IP and new session. 68 $this->assertCount(1, $messages); 69 $this->assertEquals($loginuser->id, $messages[0]->useridto); 70 $this->assertEquals('newlogin', $messages[0]->eventtype); 71 } 72 73 /** 74 * Test new login notification is skipped because of same IP from last login. 75 */ 76 public function test_login_notification_skip_same_ip() { 77 global $SESSION; 78 79 $this->resetAfterTest(); 80 81 $loginuser = self::getDataGenerator()->create_user(); 82 $this->setUser(0); 83 84 // Mock data for test. 85 $SESSION->isnewsessioncookie = true; // New session cookie. 86 @complete_user_login($loginuser); 87 88 // Redirect messages to sink and stop buffer output from CLI task. 89 $sink = $this->redirectMessages(); 90 ob_start(); 91 $this->runAdhocTasks('\core\task\send_login_notifications'); 92 $output = ob_get_contents(); 93 ob_end_clean(); 94 $messages = $sink->get_messages(); 95 $sink->close(); 96 97 // Skip notification when we have the same previous IP even if the browser used to connect is new. 98 $this->assertCount(0, $messages); 99 } 100 101 /** 102 * Test new login notification is skipped because of same browser from last login. 103 */ 104 public function test_login_notification_skip_same_browser() { 105 global $SESSION; 106 107 $this->resetAfterTest(); 108 109 $loginuser = self::getDataGenerator()->create_user(); 110 $this->setUser(0); 111 112 // Mock data for test. 113 $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. 114 $SESSION->isnewsessioncookie = false; 115 @complete_user_login($loginuser); 116 117 // Redirect messages to sink and stop buffer output from CLI task. 118 $sink = $this->redirectMessages(); 119 ob_start(); 120 $this->runAdhocTasks('\core\task\send_login_notifications'); 121 $output = ob_get_contents(); 122 ob_end_clean(); 123 $messages = $sink->get_messages(); 124 $sink->close(); 125 126 // Skip notification, different ip but same browser (probably, mobile phone browser). 127 $this->assertCount(0, $messages); 128 } 129 130 /** 131 * Test new login notification is skipped because of auto-login from the mobile app (skip duplicated notifications). 132 */ 133 public function test_login_notification_skip_mobileapp() { 134 global $SESSION; 135 136 $this->resetAfterTest(); 137 138 $loginuser = self::getDataGenerator()->create_user(); 139 $this->setUser(0); 140 141 // Mock data for test. 142 $loginuser->lastip = '1.2.3.4.6'; // Different ip that current. 143 $SESSION->isnewsessioncookie = true; // New session cookie. 144 \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. 145 @complete_user_login($loginuser); 146 147 // Redirect messages to sink and stop buffer output from CLI task. 148 $sink = $this->redirectMessages(); 149 ob_start(); 150 $this->runAdhocTasks('\core\task\send_login_notifications'); 151 $output = ob_get_contents(); 152 ob_end_clean(); 153 $messages = $sink->get_messages(); 154 $sink->close(); 155 156 $this->assertCount(0, $messages); 157 } 158 159 /** 160 * Test new mobile app login notification. 161 */ 162 public function test_mobile_app_login_notification() { 163 global $USER, $DB, $SESSION; 164 165 $this->resetAfterTest(); 166 167 $loginuser = self::getDataGenerator()->create_user(); 168 $this->setUser($loginuser); 169 170 // Mock data for test. 171 $USER->lastip = '1.2.3.4.6'; // Different ip that current. 172 173 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 174 $token = external_generate_token_for_current_user($service); 175 \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. 176 177 // Simulate we are using an new device. 178 $fakedevice = (object) [ 179 'userid' => $USER->id, 180 'appid' => 'com.moodle.moodlemobile', 181 'name' => 'occam', 182 'model' => 'Nexus 4', 183 'platform' => 'Android', 184 'version' => '4.2.2', 185 'pushid' => 'kishUhd', 186 'uuid' => 'KIhud7s', 187 'timecreated' => time() + MINSECS, 188 'timemodified' => time() + MINSECS 189 ]; 190 $DB->insert_record('user_devices', $fakedevice); 191 192 external_log_token_request($token); 193 194 // Redirect messages to sink and stop buffer output from CLI task. 195 $sink = $this->redirectMessages(); 196 ob_start(); 197 $this->runAdhocTasks('\core\task\send_login_notifications'); 198 $output = ob_get_contents(); 199 ob_end_clean(); 200 $messages = $sink->get_messages(); 201 $sink->close(); 202 203 // We sent a login notification because we are using a new device and different IP. 204 $this->assertCount(1, $messages); 205 $this->assertEquals($loginuser->id, $messages[0]->useridto); 206 $this->assertEquals('newlogin', $messages[0]->eventtype); 207 } 208 209 /** 210 * Test new mobile app login notification skipped becase of same last ip. 211 */ 212 public function test_mobile_app_login_notification_skip_same_ip() { 213 global $USER, $DB, $SESSION; 214 215 $this->resetAfterTest(); 216 217 $loginuser = self::getDataGenerator()->create_user(); 218 $this->setUser($loginuser); 219 220 // Mock data for test. 221 $USER->lastip = '0.0.0.0'; 222 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 223 $token = external_generate_token_for_current_user($service); 224 \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. 225 226 // Simulate we are using an new device. 227 $fakedevice = (object) [ 228 'userid' => $USER->id, 229 'appid' => 'com.moodle.moodlemobile', 230 'name' => 'occam', 231 'model' => 'Nexus 4', 232 'platform' => 'Android', 233 'version' => '4.2.2', 234 'pushid' => 'kishUhd', 235 'uuid' => 'KIhud7s', 236 'timecreated' => time() + MINSECS, 237 'timemodified' => time() + MINSECS 238 ]; 239 $DB->insert_record('user_devices', $fakedevice); 240 241 external_log_token_request($token); 242 243 // Redirect messages to sink and stop buffer output from CLI task. 244 $sink = $this->redirectMessages(); 245 ob_start(); 246 $this->runAdhocTasks('\core\task\send_login_notifications'); 247 $output = ob_get_contents(); 248 ob_end_clean(); 249 $messages = $sink->get_messages(); 250 $sink->close(); 251 252 // While using the same IP avoid sending new login notifications even if we are using a new device. 253 $this->assertCount(0, $messages); 254 } 255 256 /** 257 * Test new mobile app login notification skipped becase of same device. 258 */ 259 public function test_mobile_app_login_notification_skip_same_device() { 260 global $USER, $DB, $SESSION; 261 262 $this->resetAfterTest(); 263 264 $loginuser = self::getDataGenerator()->create_user(); 265 $this->setUser($loginuser); 266 267 // Mock data for test. 268 $USER->lastip = '1.2.3.4.6'; // New ip. 269 $service = $DB->get_record('external_services', array('shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE)); 270 $token = external_generate_token_for_current_user($service); 271 \core_useragent::instance(true, 'MoodleMobile'); // Force fake mobile app user agent. 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 device avoid sending new login notifications even if the IP changes. 285 $this->assertCount(0, $messages); 286 } 287 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body