Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   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      }
 606  
 607      public function test_is_loggedinas() {
 608          $this->resetAfterTest();
 609  
 610          $user1 = $this->getDataGenerator()->create_user();
 611          $user2 = $this->getDataGenerator()->create_user();
 612  
 613          $this->assertFalse(\core\session\manager::is_loggedinas());
 614  
 615          $this->setUser($user1);
 616          \core\session\manager::loginas($user2->id, \context_system::instance());
 617  
 618          $this->assertTrue(\core\session\manager::is_loggedinas());
 619      }
 620  
 621      public function test_get_realuser() {
 622          $this->resetAfterTest();
 623  
 624          $user1 = $this->getDataGenerator()->create_user();
 625          $user2 = $this->getDataGenerator()->create_user();
 626  
 627          $this->setUser($user1);
 628          $normal = \core\session\manager::get_realuser();
 629          $this->assertSame($GLOBALS['USER'], $normal);
 630  
 631          \core\session\manager::loginas($user2->id, \context_system::instance());
 632  
 633          $real = \core\session\manager::get_realuser();
 634  
 635          unset($real->password);
 636          unset($real->description);
 637          unset($real->sesskey);
 638          unset($user1->password);
 639          unset($user1->description);
 640          unset($user1->sesskey);
 641  
 642          $this->assertEquals($real, $user1);
 643          $this->assertSame($_SESSION['REALUSER'], $real);
 644      }
 645  
 646      /**
 647       * Session lock info on pages.
 648       *
 649       * @return array
 650       */
 651      public function pages_sessionlocks() {
 652          return [
 653              [
 654                  'url'      => '/good.php',
 655                  'start'    => 1500000001.000,
 656                  'gained'   => 1500000002.000,
 657                  'released' => 1500000003.000,
 658                  'wait'     => 1.0,
 659                  'held'     => 1.0
 660              ],
 661              [
 662                  'url'      => '/bad.php?wait=5',
 663                  'start'    => 1500000003.000,
 664                  'gained'   => 1500000005.000,
 665                  'released' => 1500000007.000,
 666                  'held'     => 2.0,
 667                  'wait'     => 2.0
 668              ]
 669          ];
 670      }
 671  
 672      /**
 673       * Test to get recent session locks.
 674       */
 675      public function test_get_recent_session_locks() {
 676          global $CFG;
 677  
 678          $this->resetAfterTest();
 679          $CFG->debugsessionlock = 5;
 680          $pages = $this->pages_sessionlocks();
 681          // Recent session locks must be empty at first.
 682          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 683          $this->assertEmpty($recentsessionlocks);
 684  
 685          // Add page to the recentsessionlocks array.
 686          \core\session\manager::update_recent_session_locks($pages[0]);
 687          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 688          // Make sure we are getting the first page we added.
 689          $this->assertEquals($pages[0], $recentsessionlocks[0]);
 690          // There should be 1 page in the array.
 691          $this->assertCount(1, $recentsessionlocks);
 692  
 693          // Add second page to the recentsessionlocks array.
 694          \core\session\manager::update_recent_session_locks($pages[1]);
 695          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 696          // Make sure we are getting the second page we added.
 697          $this->assertEquals($pages[1], $recentsessionlocks[1]);
 698          // There should be 2 pages in the array.
 699          $this->assertCount(2, $recentsessionlocks);
 700      }
 701  
 702      /**
 703       * Test to update recent session locks.
 704       */
 705      public function test_update_recent_session_locks() {
 706          global $CFG;
 707  
 708          $this->resetAfterTest();
 709          $CFG->debugsessionlock = 5;
 710          $pages = $this->pages_sessionlocks();
 711  
 712          \core\session\manager::update_recent_session_locks($pages[0]);
 713          \core\session\manager::update_recent_session_locks($pages[1]);
 714          $recentsessionlocks = \core\session\manager::get_recent_session_locks();
 715          // There should be 2 pages in the array.
 716          $this->assertCount(2, $recentsessionlocks);
 717          // Make sure the last page is added at the end of the array.
 718          $this->assertEquals($pages[1], end($recentsessionlocks));
 719  
 720      }
 721  
 722      /**
 723       * Test to get session lock info.
 724       */
 725      public function test_get_session_lock_info() {
 726          global $PERF;
 727  
 728          $this->resetAfterTest();
 729  
 730          $pages = $this->pages_sessionlocks();
 731          $PERF->sessionlock = $pages[0];
 732          $sessionlock = \core\session\manager::get_session_lock_info();
 733          $this->assertEquals($pages[0], $sessionlock);
 734      }
 735  
 736      /**
 737       * Session lock info on some pages to serve as history.
 738       *
 739       * @return array
 740       */
 741      public function sessionlock_history() {
 742          return [
 743              [
 744                  'url'      => '/good.php',
 745                  'start'    => 1500000001.000,
 746                  'gained'   => 1500000001.100,
 747                  'released' => 1500000001.500,
 748                  'wait'     => 0.1
 749              ],
 750              [
 751                  // This bad request doesn't release the session for 10 seconds.
 752                  'url'      => '/bad.php',
 753                  'start'    => 1500000012.000,
 754                  'gained'   => 1500000012.200,
 755                  'released' => 1500000020.200,
 756                  'wait'     => 0.2
 757              ],
 758              [
 759                  // All subsequent requests are blocked and need to wait.
 760                  'url'      => '/good.php?id=1',
 761                  'start'    => 1500000012.900,
 762                  'gained'   => 1500000020.200,
 763                  'released' => 1500000022.000,
 764                  'wait'     => 7.29
 765              ],
 766              [
 767                  'url'      => '/good.php?id=2',
 768                  'start'    => 1500000014.000,
 769                  'gained'   => 1500000022.000,
 770                  'released' => 1500000025.000,
 771                  'wait'     => 8.0
 772              ],
 773              [
 774                  'url'      => '/good.php?id=3',
 775                  'start'    => 1500000015.000,
 776                  'gained'   => 1500000025.000,
 777                  'released' => 1500000026.000,
 778                  'wait'     => 10.0
 779              ],
 780              [
 781                  'url'      => '/good.php?id=4',
 782                  'start'    => 1500000016.000,
 783                  'gained'   => 1500000026.000,
 784                  'released' => 1500000027.000,
 785                  'wait'     => 10.0
 786              ]
 787          ];
 788      }
 789  
 790      /**
 791       * Data provider for test_get_locked_page_at function.
 792       *
 793       * @return array
 794       */
 795      public function sessionlocks_info_provider() : array {
 796          return [
 797              [
 798                  'url'      => null,
 799                  'time'    => 1500000001.000
 800              ],
 801              [
 802                  'url'      => '/bad.php',
 803                  'time'    => 1500000014.000
 804              ],
 805              [
 806                  'url'      => '/good.php?id=2',
 807                  'time'    => 1500000022.500
 808              ],
 809          ];
 810      }
 811  
 812      /**
 813       * Test to get locked page at a speficic timestamp.
 814       *
 815       * @dataProvider sessionlocks_info_provider
 816       * @param array $url Session lock page url.
 817       * @param array $time Session lock time.
 818       */
 819      public function test_get_locked_page_at($url, $time) {
 820          global $CFG, $SESSION;
 821  
 822          $this->resetAfterTest();
 823          $CFG->debugsessionlock = 5;
 824          $SESSION->recentsessionlocks = $this->sessionlock_history();
 825  
 826          $page = \core\session\manager::get_locked_page_at($time);
 827          $this->assertEquals($url, is_array($page) ? $page['url'] : null);
 828      }
 829  
 830      /**
 831       * Test cleanup recent session locks.
 832       */
 833      public function test_cleanup_recent_session_locks() {
 834          global $CFG, $SESSION;
 835  
 836          $this->resetAfterTest();
 837          $CFG->debugsessionlock = 5;
 838  
 839          $SESSION->recentsessionlocks = $this->sessionlock_history();
 840          $this->assertCount(6, $SESSION->recentsessionlocks);
 841          \core\session\manager::cleanup_recent_session_locks();
 842          // Make sure the session history has been cleaned up and only has the latest page.
 843          $this->assertCount(1, $SESSION->recentsessionlocks);
 844          $this->assertEquals('/good.php?id=4', $SESSION->recentsessionlocks[0]['url']);
 845      }
 846  
 847      /**
 848       * Data provider for the array_session_diff function.
 849       *
 850       * @return array
 851       */
 852      public function array_session_diff_provider() {
 853          // Create an instance of this object so the comparison object's identities are the same.
 854          // Used in one of the tests below.
 855          $compareobjectb = (object) ['array' => 'b'];
 856  
 857          return [
 858              'both same objects' => [
 859                  'a' => ['example' => (object) ['array' => 'a']],
 860                  'b' => ['example' => (object) ['array' => 'a']],
 861                  'expected' => [],
 862              ],
 863              'both same arrays' => [
 864                  'a' => ['example' => ['array' => 'a']],
 865                  'b' => ['example' => ['array' => 'a']],
 866                  'expected' => [],
 867              ],
 868              'both the same with nested objects' => [
 869                  'a' => ['example' => (object) ['array' => 'a', 'deeper' => (object) []]],
 870                  'b' => ['example' => (object) ['array' => 'a', 'deeper' => (object) []]],
 871                  'expected' => [],
 872              ],
 873              'first array larger' => [
 874                  'a' => ['x' => 1, 'y' => 2],
 875                  'b' => ['x' => 1],
 876                  'expected' => ['y' => 2]
 877              ],
 878              'second array larger' => [
 879                  'a' => ['x' => 1],
 880                  'b' => ['x' => 1, 'y' => 2],
 881                  'expected' => ['y' => 2]
 882              ],
 883              'objects with different values but same keys' => [
 884                  'a' => ['example' => (object) ['array' => 'a']],
 885                  'b' => ['example' => $compareobjectb],
 886                  'expected' => ['example' => $compareobjectb]
 887              ],
 888              'different arrays with top level indexes' => [
 889                  'a' => ['x', 'y'],
 890                  'b' => ['x', 'y', 'z'],
 891                  'expected' => [2 => 'z']
 892              ],
 893              'different types but same values as first level' => [
 894                  'a' => ['example' => (object) ['array' => 'a']],
 895                  'b' => ['example' => ['array' => 'a']],
 896                  'expected' => ['example' => ['array' => 'a']]
 897              ],
 898              'different types but same values nested' => [
 899                  'a' => ['example' => (object) ['array' => ['a' => 'test']]],
 900                  'b' => ['example' => (object) ['array' => (object) ['a' => 'test']]],
 901                  // Type checking is not done further than the first level, so we expect no difference.
 902                  'expected' => []
 903              ]
 904          ];
 905      }
 906  
 907      /**
 908       * Tests array diff method in various situations.
 909       *
 910       * @dataProvider array_session_diff_provider
 911       * @covers \core\session\manager::array_session_diff
 912       * @param array $a first value.
 913       * @param array $b second value to compare to $a.
 914       * @param array $expected the expected difference.
 915       */
 916      public function test_array_session_diff(array $a, array $b, array $expected) {
 917          $class = new \ReflectionClass('\core\session\manager');
 918          $method = $class->getMethod('array_session_diff');
 919          $method->setAccessible(true);
 920  
 921          $result = $method->invokeArgs(null, [$a, $b]);
 922          $this->assertSame($expected, $result);
 923      }
 924  }