Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401] [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;
  18  
  19  /**
  20   * Unit tests for session manager class.
  21   *
  22   * @package    core
  23   * @category   test
  24   * @copyright  2013 Petr Skoda {@link http://skodak.org}
  25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  class session_manager_test extends \advanced_testcase {
  28      public function test_start() {
  29          $this->resetAfterTest();
  30          // Session must be started only once...
  31          \core\session\manager::start();
  32          $this->assertDebuggingCalled('Session was already started!', DEBUG_DEVELOPER);
  33      }
  34  
  35      public function test_init_empty_session() {
  36          global $SESSION, $USER;
  37          $this->resetAfterTest();
  38  
  39          $user = $this->getDataGenerator()->create_user();
  40  
  41          $SESSION->test = true;
  42          $this->assertTrue($GLOBALS['SESSION']->test);
  43          $this->assertTrue($_SESSION['SESSION']->test);
  44  
  45          \core\session\manager::set_user($user);
  46          $this->assertSame($user, $USER);
  47          $this->assertSame($user, $GLOBALS['USER']);
  48          $this->assertSame($user, $_SESSION['USER']);
  49  
  50          \core\session\manager::init_empty_session();
  51  
  52          $this->assertInstanceOf('stdClass', $SESSION);
  53          $this->assertEmpty((array)$SESSION);
  54          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
  55          $this->assertSame($GLOBALS['SESSION'], $SESSION);
  56  
  57          $this->assertInstanceOf('stdClass', $USER);
  58          $this->assertEqualsCanonicalizing(array('id' => 0, 'mnethostid' => 1), (array)$USER);
  59          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
  60          $this->assertSame($GLOBALS['USER'], $USER);
  61  
  62          // Now test how references work.
  63  
  64          $GLOBALS['SESSION'] = new \stdClass();
  65          $GLOBALS['SESSION']->test = true;
  66          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
  67          $this->assertSame($GLOBALS['SESSION'], $SESSION);
  68  
  69          $SESSION = new \stdClass();
  70          $SESSION->test2 = true;
  71          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
  72          $this->assertSame($GLOBALS['SESSION'], $SESSION);
  73  
  74          $_SESSION['SESSION'] = new \stdClass();
  75          $_SESSION['SESSION']->test3 = true;
  76          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
  77          $this->assertSame($GLOBALS['SESSION'], $SESSION);
  78  
  79          $GLOBALS['USER'] = new \stdClass();
  80          $GLOBALS['USER']->test = true;
  81          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
  82          $this->assertSame($GLOBALS['USER'], $USER);
  83  
  84          $USER = new \stdClass();
  85          $USER->test2 = true;
  86          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
  87          $this->assertSame($GLOBALS['USER'], $USER);
  88  
  89          $_SESSION['USER'] = new \stdClass();
  90          $_SESSION['USER']->test3 = true;
  91          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
  92          $this->assertSame($GLOBALS['USER'], $USER);
  93      }
  94  
  95      public function test_set_user() {
  96          global $USER;
  97          $this->resetAfterTest();
  98  
  99          $this->assertEquals(0, $USER->id);
 100  
 101          $user = $this->getDataGenerator()->create_user();
 102          $this->assertObjectHasAttribute('description', $user);
 103          $this->assertObjectHasAttribute('password', $user);
 104  
 105          \core\session\manager::set_user($user);
 106  
 107          $this->assertEquals($user->id, $USER->id);
 108          $this->assertObjectNotHasAttribute('description', $user);
 109          $this->assertObjectNotHasAttribute('password', $user);
 110          $this->assertObjectHasAttribute('sesskey', $user);
 111          $this->assertSame($user, $GLOBALS['USER']);
 112          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 113          $this->assertSame($GLOBALS['USER'], $USER);
 114      }
 115  
 116      public function test_login_user() {
 117          global $USER;
 118          $this->resetAfterTest();
 119  
 120          $this->assertEquals(0, $USER->id);
 121  
 122          $user = $this->getDataGenerator()->create_user();
 123  
 124          @\core\session\manager::login_user($user); // Ignore header error messages.
 125          $this->assertEquals($user->id, $USER->id);
 126  
 127          $this->assertObjectNotHasAttribute('description', $user);
 128          $this->assertObjectNotHasAttribute('password', $user);
 129          $this->assertSame($user, $GLOBALS['USER']);
 130          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 131          $this->assertSame($GLOBALS['USER'], $USER);
 132      }
 133  
 134      public function test_terminate_current() {
 135          global $USER, $SESSION;
 136          $this->resetAfterTest();
 137  
 138          $this->setAdminUser();
 139          \core\session\manager::terminate_current();
 140          $this->assertEquals(0, $USER->id);
 141  
 142          $this->assertInstanceOf('stdClass', $SESSION);
 143          $this->assertEmpty((array)$SESSION);
 144          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
 145          $this->assertSame($GLOBALS['SESSION'], $SESSION);
 146  
 147          $this->assertInstanceOf('stdClass', $USER);
 148          $this->assertEqualsCanonicalizing(array('id' => 0, 'mnethostid' => 1), (array)$USER);
 149          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 150          $this->assertSame($GLOBALS['USER'], $USER);
 151      }
 152  
 153      public function test_write_close() {
 154          global $USER;
 155          $this->resetAfterTest();
 156  
 157          // Just make sure no errors and $USER->id is kept
 158          $this->setAdminUser();
 159          $userid = $USER->id;
 160          \core\session\manager::write_close();
 161          $this->assertSame($userid, $USER->id);
 162  
 163          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 164          $this->assertSame($GLOBALS['USER'], $USER);
 165      }
 166  
 167      public function test_session_exists() {
 168          global $CFG, $DB;
 169          $this->resetAfterTest();
 170  
 171          $this->assertFalse(\core\session\manager::session_exists('abc'));
 172  
 173          $user = $this->getDataGenerator()->create_user();
 174          $guest = guest_user();
 175  
 176          // The file handler is used by default, so let's fake the data somehow.
 177          $sid = md5('hokus');
 178          mkdir("$CFG->dataroot/sessions/", $CFG->directorypermissions, true);
 179          touch("$CFG->dataroot/sessions/sess_$sid");
 180  
 181          $this->assertFalse(\core\session\manager::session_exists($sid));
 182  
 183          $record = new \stdClass();
 184          $record->userid = 0;
 185          $record->sid = $sid;
 186          $record->timecreated = time();
 187          $record->timemodified = $record->timecreated;
 188          $record->id = $DB->insert_record('sessions', $record);
 189  
 190          $this->assertTrue(\core\session\manager::session_exists($sid));
 191  
 192          $record->timecreated = time() - $CFG->sessiontimeout - 100;
 193          $record->timemodified = $record->timecreated + 10;
 194          $DB->update_record('sessions', $record);
 195  
 196          $this->assertTrue(\core\session\manager::session_exists($sid));
 197  
 198          $record->userid = $guest->id;
 199          $DB->update_record('sessions', $record);
 200  
 201          $this->assertTrue(\core\session\manager::session_exists($sid));
 202  
 203          $record->userid = $user->id;
 204          $DB->update_record('sessions', $record);
 205  
 206          $this->assertFalse(\core\session\manager::session_exists($sid));
 207  
 208          $CFG->sessiontimeout = $CFG->sessiontimeout + 3000;
 209  
 210          $this->assertTrue(\core\session\manager::session_exists($sid));
 211      }
 212  
 213      public function test_touch_session() {
 214          global $DB;
 215          $this->resetAfterTest();
 216  
 217          $sid = md5('hokus');
 218          $record = new \stdClass();
 219          $record->state        = 0;
 220          $record->sid          = $sid;
 221          $record->sessdata     = null;
 222          $record->userid       = 2;
 223          $record->timecreated  = time() - 60*60;
 224          $record->timemodified = time() - 30;
 225          $record->firstip      = $record->lastip = '10.0.0.1';
 226          $record->id = $DB->insert_record('sessions', $record);
 227  
 228          $now = time();
 229          \core\session\manager::touch_session($sid);
 230          $updated = $DB->get_field('sessions', 'timemodified', array('id'=>$record->id));
 231  
 232          $this->assertGreaterThanOrEqual($now, $updated);
 233          $this->assertLessThanOrEqual(time(), $updated);
 234      }
 235  
 236      public function test_kill_session() {
 237          global $DB, $USER;
 238          $this->resetAfterTest();
 239  
 240          $this->setAdminUser();
 241          $userid = $USER->id;
 242  
 243          $sid = md5('hokus');
 244          $record = new \stdClass();
 245          $record->state        = 0;
 246          $record->sid          = $sid;
 247          $record->sessdata     = null;
 248          $record->userid       = $userid;
 249          $record->timecreated  = time() - 60*60;
 250          $record->timemodified = time() - 30;
 251          $record->firstip      = $record->lastip = '10.0.0.1';
 252          $DB->insert_record('sessions', $record);
 253  
 254          $record->userid       = 0;
 255          $record->sid          = md5('pokus');
 256          $DB->insert_record('sessions', $record);
 257  
 258          $this->assertEquals(2, $DB->count_records('sessions'));
 259  
 260          \core\session\manager::kill_session($sid);
 261  
 262          $this->assertEquals(1, $DB->count_records('sessions'));
 263          $this->assertFalse($DB->record_exists('sessions', array('sid'=>$sid)));
 264  
 265          $this->assertSame($userid, $USER->id);
 266      }
 267  
 268      public function test_kill_user_sessions() {
 269          global $DB, $USER;
 270          $this->resetAfterTest();
 271  
 272          $this->setAdminUser();
 273          $userid = $USER->id;
 274  
 275          $sid = md5('hokus');
 276          $record = new \stdClass();
 277          $record->state        = 0;
 278          $record->sid          = $sid;
 279          $record->sessdata     = null;
 280          $record->userid       = $userid;
 281          $record->timecreated  = time() - 60*60;
 282          $record->timemodified = time() - 30;
 283          $record->firstip      = $record->lastip = '10.0.0.1';
 284          $DB->insert_record('sessions', $record);
 285  
 286          $record->sid          = md5('hokus2');
 287          $DB->insert_record('sessions', $record);
 288  
 289          $record->userid       = 0;
 290          $record->sid          = md5('pokus');
 291          $DB->insert_record('sessions', $record);
 292  
 293          $this->assertEquals(3, $DB->count_records('sessions'));
 294  
 295          \core\session\manager::kill_user_sessions($userid);
 296  
 297          $this->assertEquals(1, $DB->count_records('sessions'));
 298          $this->assertFalse($DB->record_exists('sessions', array('userid' => $userid)));
 299  
 300          $record->userid       = $userid;
 301          $record->sid          = md5('pokus3');
 302          $DB->insert_record('sessions', $record);
 303  
 304          $record->userid       = $userid;
 305          $record->sid          = md5('pokus4');
 306          $DB->insert_record('sessions', $record);
 307  
 308          $record->userid       = $userid;
 309          $record->sid          = md5('pokus5');
 310          $DB->insert_record('sessions', $record);
 311  
 312          $this->assertEquals(3, $DB->count_records('sessions', array('userid' => $userid)));
 313  
 314          \core\session\manager::kill_user_sessions($userid, md5('pokus5'));
 315  
 316          $this->assertEquals(1, $DB->count_records('sessions', array('userid' => $userid)));
 317          $this->assertEquals(1, $DB->count_records('sessions', array('userid' => $userid, 'sid' => md5('pokus5'))));
 318      }
 319  
 320      public function test_apply_concurrent_login_limit() {
 321          global $DB;
 322          $this->resetAfterTest();
 323  
 324          $user1 = $this->getDataGenerator()->create_user();
 325          $user2 = $this->getDataGenerator()->create_user();
 326          $guest = guest_user();
 327  
 328          $record = new \stdClass();
 329          $record->state        = 0;
 330          $record->sessdata     = null;
 331          $record->userid       = $user1->id;
 332          $record->timemodified = time();
 333          $record->firstip      = $record->lastip = '10.0.0.1';
 334  
 335          $record->sid = md5('hokus1');
 336          $record->timecreated = 20;
 337          $DB->insert_record('sessions', $record);
 338          $record->sid = md5('hokus2');
 339          $record->timecreated = 10;
 340          $DB->insert_record('sessions', $record);
 341          $record->sid = md5('hokus3');
 342          $record->timecreated = 30;
 343          $DB->insert_record('sessions', $record);
 344  
 345          $record->userid = $user2->id;
 346          $record->sid = md5('pokus1');
 347          $record->timecreated = 20;
 348          $DB->insert_record('sessions', $record);
 349          $record->sid = md5('pokus2');
 350          $record->timecreated = 10;
 351          $DB->insert_record('sessions', $record);
 352          $record->sid = md5('pokus3');
 353          $record->timecreated = 30;
 354          $DB->insert_record('sessions', $record);
 355  
 356          $record->timecreated = 10;
 357          $record->userid = $guest->id;
 358          $record->sid = md5('g1');
 359          $DB->insert_record('sessions', $record);
 360          $record->sid = md5('g2');
 361          $DB->insert_record('sessions', $record);
 362          $record->sid = md5('g3');
 363          $DB->insert_record('sessions', $record);
 364  
 365          $record->userid = 0;
 366          $record->sid = md5('nl1');
 367          $DB->insert_record('sessions', $record);
 368          $record->sid = md5('nl2');
 369          $DB->insert_record('sessions', $record);
 370          $record->sid = md5('nl3');
 371          $DB->insert_record('sessions', $record);
 372  
 373          set_config('limitconcurrentlogins', 0);
 374          $this->assertCount(12, $DB->get_records('sessions'));
 375  
 376          \core\session\manager::apply_concurrent_login_limit($user1->id);
 377          \core\session\manager::apply_concurrent_login_limit($user2->id);
 378          \core\session\manager::apply_concurrent_login_limit($guest->id);
 379          \core\session\manager::apply_concurrent_login_limit(0);
 380          $this->assertCount(12, $DB->get_records('sessions'));
 381  
 382          set_config('limitconcurrentlogins', -1);
 383  
 384          \core\session\manager::apply_concurrent_login_limit($user1->id);
 385          \core\session\manager::apply_concurrent_login_limit($user2->id);
 386          \core\session\manager::apply_concurrent_login_limit($guest->id);
 387          \core\session\manager::apply_concurrent_login_limit(0);
 388          $this->assertCount(12, $DB->get_records('sessions'));
 389  
 390          set_config('limitconcurrentlogins', 2);
 391  
 392          \core\session\manager::apply_concurrent_login_limit($user1->id);
 393          $this->assertCount(11, $DB->get_records('sessions'));
 394          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 20)));
 395          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 30)));
 396          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 10)));
 397  
 398          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 20)));
 399          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 30)));
 400          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 10)));
 401          set_config('limitconcurrentlogins', 2);
 402          \core\session\manager::apply_concurrent_login_limit($user2->id, md5('pokus2'));
 403          $this->assertCount(10, $DB->get_records('sessions'));
 404          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 20)));
 405          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 30)));
 406          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 10)));
 407  
 408          \core\session\manager::apply_concurrent_login_limit($guest->id);
 409          \core\session\manager::apply_concurrent_login_limit(0);
 410          $this->assertCount(10, $DB->get_records('sessions'));
 411  
 412          set_config('limitconcurrentlogins', 1);
 413  
 414          \core\session\manager::apply_concurrent_login_limit($user1->id, md5('grrr'));
 415          $this->assertCount(9, $DB->get_records('sessions'));
 416          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 20)));
 417          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 30)));
 418          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 10)));
 419  
 420          \core\session\manager::apply_concurrent_login_limit($user1->id);
 421          $this->assertCount(9, $DB->get_records('sessions'));
 422          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 20)));
 423          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 30)));
 424          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user1->id, 'timecreated' => 10)));
 425  
 426          \core\session\manager::apply_concurrent_login_limit($user2->id, md5('pokus2'));
 427          $this->assertCount(8, $DB->get_records('sessions'));
 428          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 20)));
 429          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 30)));
 430          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 10)));
 431  
 432          \core\session\manager::apply_concurrent_login_limit($user2->id);
 433          $this->assertCount(8, $DB->get_records('sessions'));
 434          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 20)));
 435          $this->assertFalse($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 30)));
 436          $this->assertTrue($DB->record_exists('sessions', array('userid' => $user2->id, 'timecreated' => 10)));
 437  
 438          \core\session\manager::apply_concurrent_login_limit($guest->id);
 439          \core\session\manager::apply_concurrent_login_limit(0);
 440          $this->assertCount(8, $DB->get_records('sessions'));
 441      }
 442  
 443      public function test_kill_all_sessions() {
 444          global $DB, $USER;
 445          $this->resetAfterTest();
 446  
 447          $this->setAdminUser();
 448          $userid = $USER->id;
 449  
 450          $sid = md5('hokus');
 451          $record = new \stdClass();
 452          $record->state        = 0;
 453          $record->sid          = $sid;
 454          $record->sessdata     = null;
 455          $record->userid       = $userid;
 456          $record->timecreated  = time() - 60*60;
 457          $record->timemodified = time() - 30;
 458          $record->firstip      = $record->lastip = '10.0.0.1';
 459          $DB->insert_record('sessions', $record);
 460  
 461          $record->sid          = md5('hokus2');
 462          $DB->insert_record('sessions', $record);
 463  
 464          $record->userid       = 0;
 465          $record->sid          = md5('pokus');
 466          $DB->insert_record('sessions', $record);
 467  
 468          $this->assertEquals(3, $DB->count_records('sessions'));
 469  
 470          \core\session\manager::kill_all_sessions();
 471  
 472          $this->assertEquals(0, $DB->count_records('sessions'));
 473          $this->assertSame(0, $USER->id);
 474      }
 475  
 476      public function test_gc() {
 477          global $CFG, $DB, $USER;
 478          $this->resetAfterTest();
 479  
 480          $this->setAdminUser();
 481          $adminid = $USER->id;
 482          $this->setGuestUser();
 483          $guestid = $USER->id;
 484          $this->setUser(0);
 485  
 486          $CFG->sessiontimeout = 60*10;
 487  
 488          $record = new \stdClass();
 489          $record->state        = 0;
 490          $record->sid          = md5('hokus1');
 491          $record->sessdata     = null;
 492          $record->userid       = $adminid;
 493          $record->timecreated  = time() - 60*60;
 494          $record->timemodified = time() - 30;
 495          $record->firstip      = $record->lastip = '10.0.0.1';
 496          $r1 = $DB->insert_record('sessions', $record);
 497  
 498          $record->sid          = md5('hokus2');
 499          $record->userid       = $adminid;
 500          $record->timecreated  = time() - 60*60;
 501          $record->timemodified = time() - 60*20;
 502          $r2 = $DB->insert_record('sessions', $record);
 503  
 504          $record->sid          = md5('hokus3');
 505          $record->userid       = $guestid;
 506          $record->timecreated  = time() - 60*60*60;
 507          $record->timemodified = time() - 60*20;
 508          $r3 = $DB->insert_record('sessions', $record);
 509  
 510          $record->sid          = md5('hokus4');
 511          $record->userid       = $guestid;
 512          $record->timecreated  = time() - 60*60*60;
 513          $record->timemodified = time() - 60*10*5 - 60;
 514          $r4 = $DB->insert_record('sessions', $record);
 515  
 516          $record->sid          = md5('hokus5');
 517          $record->userid       = 0;
 518          $record->timecreated  = time() - 60*5;
 519          $record->timemodified = time() - 60*5;
 520          $r5 = $DB->insert_record('sessions', $record);
 521  
 522          $record->sid          = md5('hokus6');
 523          $record->userid       = 0;
 524          $record->timecreated  = time() - 60*60;
 525          $record->timemodified = time() - 60*10 -10;
 526          $r6 = $DB->insert_record('sessions', $record);
 527  
 528          $record->sid          = md5('hokus7');
 529          $record->userid       = 0;
 530          $record->timecreated  = time() - 60*60;
 531          $record->timemodified = time() - 60*9;
 532          $r7 = $DB->insert_record('sessions', $record);
 533  
 534          \core\session\manager::gc();
 535  
 536          $this->assertTrue($DB->record_exists('sessions', array('id'=>$r1)));
 537          $this->assertFalse($DB->record_exists('sessions', array('id'=>$r2)));
 538          $this->assertTrue($DB->record_exists('sessions', array('id'=>$r3)));
 539          $this->assertFalse($DB->record_exists('sessions', array('id'=>$r4)));
 540          $this->assertFalse($DB->record_exists('sessions', array('id'=>$r5)));
 541          $this->assertFalse($DB->record_exists('sessions', array('id'=>$r6)));
 542          $this->assertTrue($DB->record_exists('sessions', array('id'=>$r7)));
 543      }
 544  
 545      /**
 546       * Test loginas.
 547       * @copyright  2103 Rajesh Taneja <rajesh@moodle.com>
 548       */
 549      public function test_loginas() {
 550          global $USER, $SESSION;
 551          $this->resetAfterTest();
 552  
 553          // Set current user as Admin user and save it for later use.
 554          $this->setAdminUser();
 555          $adminuser = $USER;
 556          $adminsession = $SESSION;
 557          $user = $this->getDataGenerator()->create_user();
 558          $_SESSION['extra'] = true;
 559  
 560          // Try admin loginas this user in system context.
 561          $this->assertObjectNotHasAttribute('realuser', $USER);
 562          \core\session\manager::loginas($user->id, \context_system::instance());
 563  
 564          $this->assertSame($user->id, $USER->id);
 565          $this->assertEquals(\context_system::instance(), $USER->loginascontext);
 566          $this->assertSame($adminuser->id, $USER->realuser);
 567          $this->assertSame($GLOBALS['USER'], $_SESSION['USER']);
 568          $this->assertSame($GLOBALS['USER'], $USER);
 569          $this->assertNotSame($adminuser, $_SESSION['REALUSER']);
 570          $this->assertEquals($adminuser, $_SESSION['REALUSER']);
 571  
 572          $this->assertSame($GLOBALS['SESSION'], $_SESSION['SESSION']);
 573          $this->assertSame($GLOBALS['SESSION'], $SESSION);
 574          $this->assertNotSame($adminsession, $_SESSION['REALSESSION']);
 575          $this->assertEquals($adminsession, $_SESSION['REALSESSION']);
 576  
 577          $this->assertArrayNotHasKey('extra', $_SESSION);
 578  
 579          // Set user as current user and login as admin user in course context.
 580          \core\session\manager::init_empty_session();
 581          $this->setUser($user);
 582          $this->assertNotEquals($adminuser->id, $USER->id);
 583          $course = $this->getDataGenerator()->create_course();
 584          $coursecontext = \context_course::instance($course->id);
 585  
 586          // Catch event triggered.
 587          $sink = $this->redirectEvents();
 588          \core\session\manager::loginas($adminuser->id, $coursecontext);
 589          $events = $sink->get_events();
 590          $sink->close();
 591          $event = array_pop($events);
 592  
 593          $this->assertSame($adminuser->id, $USER->id);
 594          $this->assertSame($coursecontext, $USER->loginascontext);
 595          $this->assertSame($user->id, $USER->realuser);
 596  
 597          // Test event captured has proper information.
 598          $this->assertInstanceOf('\core\event\user_loggedinas', $event);
 599          $this->assertSame($user->id, $event->objectid);
 600          $this->assertSame($adminuser->id, $event->relateduserid);
 601          $this->assertSame($course->id, $event->courseid);
 602          $this->assertEquals($coursecontext, $event->get_context());
 603          $oldfullname = fullname($user, true);
 604          $newfullname = fullname($adminuser, true);
 605          $expectedlogdata = array($course->id, "course", "loginas", "../user/view.php?id=$course->id&amp;user=$user->id", "$oldfullname -> $newfullname");
 606          $this->assertEventLegacyLogData($expectedlogdata, $event);
 607      }
 608  
 609      public function test_is_loggedinas() {
 610          $this->resetAfterTest();
 611  
 612          $user1 = $this->getDataGenerator()->create_user();
 613          $user2 = $this->getDataGenerator()->create_user();
 614  
 615          $this->assertFalse(\core\session\manager::is_loggedinas());
 616  
 617          $this->setUser($user1);
 618          \core\session\manager::loginas($user2->id, \context_system::instance());
 619  
 620          $this->assertTrue(\core\session\manager::is_loggedinas());
 621      }
 622  
 623      public function test_get_realuser() {
 624          $this->resetAfterTest();
 625  
 626          $user1 = $this->getDataGenerator()->create_user();
 627          $user2 = $this->getDataGenerator()->create_user();
 628  
 629          $this->setUser($user1);
 630          $normal = \core\session\manager::get_realuser();
 631          $this->assertSame($GLOBALS['USER'], $normal);
 632  
 633          \core\session\manager::loginas($user2->id, \context_system::instance());
 634  
 635          $real = \core\session\manager::get_realuser();
 636  
 637          unset($real->password);
 638          unset($real->description);
 639          unset($real->sesskey);
 640          unset($user1->password);
 641          unset($user1->description);
 642          unset($user1->sesskey);
 643  
 644          $this->assertEquals($real, $user1);
 645          $this->assertSame($_SESSION['REALUSER'], $real);
 646      }
 647  
 648      /**
 649       * Session lock info on pages.
 650       *
 651       * @return array
 652       */
 653      public function pages_sessionlocks() {
 654          return [
 655              [
 656                  'url'      => '/good.php',
 657                  'start'    => 1500000001.000,
 658                  'gained'   => 1500000002.000,
 659                  'released' => 1500000003.000,
 660                  'wait'     => 1.0,
 661                  'held'     => 1.0
 662              ],
 663              [
 664                  'url'      => '/bad.php?wait=5',
 665                  'start'    => 1500000003.000,
 666                  'gained'   => 1500000005.000,
 667                  'released' => 1500000007.000,
 668                  'held'     => 2.0,
 669                  'wait'     => 2.0
 670              ]
 671          ];
 672      }
 673  
 674      /**
 675       * Test to get recent session locks.
 676       */
 677      public function test_get_recent_session_locks() {
 678          global $CFG;
 679  
 680          $this->resetAfterTest();
 681          $CFG->debugsessionlock = 5;
 682          $pages = $this->pages_sessionlocks();
 683          // Recent session locks must be empty at first.
 684          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 685          $this->assertEmpty($recentsessionlocks);
 686  
 687          // Add page to the recentsessionlocks array.
 688          \core\session\manager::update_recent_session_locks($pages[0]);
 689          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 690          // Make sure we are getting the first page we added.
 691          $this->assertEquals($pages[0], $recentsessionlocks[0]);
 692          // There should be 1 page in the array.
 693          $this->assertCount(1, $recentsessionlocks);
 694  
 695          // Add second page to the recentsessionlocks array.
 696          \core\session\manager::update_recent_session_locks($pages[1]);
 697          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 698          // Make sure we are getting the second page we added.
 699          $this->assertEquals($pages[1], $recentsessionlocks[1]);
 700          // There should be 2 pages in the array.
 701          $this->assertCount(2, $recentsessionlocks);
 702      }
 703  
 704      /**
 705       * Test to update recent session locks.
 706       */
 707      public function test_update_recent_session_locks() {
 708          global $CFG;
 709  
 710          $this->resetAfterTest();
 711          $CFG->debugsessionlock = 5;
 712          $pages = $this->pages_sessionlocks();
 713  
 714          \core\session\manager::update_recent_session_locks($pages[0]);
 715          \core\session\manager::update_recent_session_locks($pages[1]);
 716          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 717          // There should be 2 pages in the array.
 718          $this->assertCount(2, $recentsessionlocks);
 719          // Make sure the last page is added at the end of the array.
 720          $this->assertEquals($pages[1], end($recentsessionlocks));
 721  
 722      }
 723  
 724      /**
 725       * Test to get session lock info.
 726       */
 727      public function test_get_session_lock_info() {
 728          global $PERF;
 729  
 730          $this->resetAfterTest();
 731  
 732          $pages = $this->pages_sessionlocks();
 733          $PERF->sessionlock = $pages[0];
 734          $sessionlock = \core\session\manager::get_session_lock_info();
 735          $this->assertEquals($pages[0], $sessionlock);
 736      }
 737  
 738      /**
 739       * Session lock info on some pages to serve as history.
 740       *
 741       * @return array
 742       */
 743      public function sessionlock_history() {
 744          return [
 745              [
 746                  'url'      => '/good.php',
 747                  'start'    => 1500000001.000,
 748                  'gained'   => 1500000001.100,
 749                  'released' => 1500000001.500,
 750                  'wait'     => 0.1
 751              ],
 752              [
 753                  // This bad request doesn't release the session for 10 seconds.
 754                  'url'      => '/bad.php',
 755                  'start'    => 1500000012.000,
 756                  'gained'   => 1500000012.200,
 757                  'released' => 1500000020.200,
 758                  'wait'     => 0.2
 759              ],
 760              [
 761                  // All subsequent requests are blocked and need to wait.
 762                  'url'      => '/good.php?id=1',
 763                  'start'    => 1500000012.900,
 764                  'gained'   => 1500000020.200,
 765                  'released' => 1500000022.000,
 766                  'wait'     => 7.29
 767              ],
 768              [
 769                  'url'      => '/good.php?id=2',
 770                  'start'    => 1500000014.000,
 771                  'gained'   => 1500000022.000,
 772                  'released' => 1500000025.000,
 773                  'wait'     => 8.0
 774              ],
 775              [
 776                  'url'      => '/good.php?id=3',
 777                  'start'    => 1500000015.000,
 778                  'gained'   => 1500000025.000,
 779                  'released' => 1500000026.000,
 780                  'wait'     => 10.0
 781              ],
 782              [
 783                  'url'      => '/good.php?id=4',
 784                  'start'    => 1500000016.000,
 785                  'gained'   => 1500000026.000,
 786                  'released' => 1500000027.000,
 787                  'wait'     => 10.0
 788              ]
 789          ];
 790      }
 791  
 792      /**
 793       * Data provider for test_get_locked_page_at function.
 794       *
 795       * @return array
 796       */
 797      public function sessionlocks_info_provider() : array {
 798          return [
 799              [
 800                  'url'      => null,
 801                  'time'    => 1500000001.000
 802              ],
 803              [
 804                  'url'      => '/bad.php',
 805                  'time'    => 1500000014.000
 806              ],
 807              [
 808                  'url'      => '/good.php?id=2',
 809                  'time'    => 1500000022.500
 810              ],
 811          ];
 812      }
 813  
 814      /**
 815       * Test to get locked page at a speficic timestamp.
 816       *
 817       * @dataProvider sessionlocks_info_provider
 818       * @param array $url Session lock page url.
 819       * @param array $time Session lock time.
 820       */
 821      public function test_get_locked_page_at($url, $time) {
 822          global $CFG, $SESSION;
 823  
 824          $this->resetAfterTest();
 825          $CFG->debugsessionlock = 5;
 826          $SESSION->recentsessionlocks = $this->sessionlock_history();
 827  
 828          $page = \core\session\manager::get_locked_page_at($time);
 829          $this->assertEquals($url, is_array($page) ? $page['url'] : null);
 830      }
 831  
 832      /**
 833       * Test cleanup recent session locks.
 834       */
 835      public function test_cleanup_recent_session_locks() {
 836          global $CFG, $SESSION;
 837  
 838          $this->resetAfterTest();
 839          $CFG->debugsessionlock = 5;
 840  
 841          $SESSION->recentsessionlocks = $this->sessionlock_history();
 842          $this->assertCount(6, $SESSION->recentsessionlocks);
 843          \core\session\manager::cleanup_recent_session_locks();
 844          // Make sure the session history has been cleaned up and only has the latest page.
 845          $this->assertCount(1, $SESSION->recentsessionlocks);
 846          $this->assertEquals('/good.php?id=4', $SESSION->recentsessionlocks[0]['url']);
 847      }
 848  
 849      public function test_array_session_diff_same_array() {
 850          $a = [];
 851          $a['c'] = new \stdClass();
 852          $a['c']->o = new \stdClass();
 853          $a['c']->o->o = new \stdClass();
 854          $a['c']->o->o->l = 'cool';
 855  
 856          $class = new \ReflectionClass('\core\session\manager');
 857          $method = $class->getMethod('array_session_diff');
 858          $method->setAccessible(true);
 859  
 860          $result = $method->invokeArgs(null, [$a, $a]);
 861  
 862          $this->assertEmpty($result);
 863      }
 864  
 865      public function test_array_session_diff_first_array_larger() {
 866          $a = [];
 867          $a['stdClass'] = new \stdClass();
 868          $a['stdClass']->attribute = 'This is an attribute';
 869          $a['array'] = ['array', 'contents'];
 870  
 871          $b = [];
 872          $b['array'] = ['array', 'contents'];
 873  
 874          $class = new \ReflectionClass('\core\session\manager');
 875          $method = $class->getMethod('array_session_diff');
 876          $method->setAccessible(true);
 877  
 878          $result = $method->invokeArgs(null, [$a, $b]);
 879  
 880          $expected = [];
 881          $expected['stdClass'] = new \stdClass();
 882          $expected['stdClass']->attribute = 'This is an attribute';
 883          $this->assertEquals($expected, $result);
 884      }
 885  
 886      public function test_array_session_diff_second_array_larger() {
 887          $a = [];
 888          $a['array'] = ['array', 'contents'];
 889  
 890          $b = [];
 891          $b['stdClass'] = new \stdClass();
 892          $b['stdClass']->attribute = 'This is an attribute';
 893          $b['array'] = ['array', 'contents'];
 894  
 895          $class = new \ReflectionClass('\core\session\manager');
 896          $method = $class->getMethod('array_session_diff');
 897          $method->setAccessible(true);
 898  
 899          $result = $method->invokeArgs(null, [$a, $b]);
 900  
 901          // It's empty because the first array contains all the contents of the second.
 902          $expected = [];
 903          $this->assertEquals($expected, $result);
 904      }
 905  }