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_user;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  require_once($CFG->dirroot.'/user/lib.php');
  23  
  24  /**
  25   * Unit tests for user lib api.
  26   *
  27   * @package    core_user
  28   * @category   test
  29   * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
  30   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   */
  32  class userlib_test extends \advanced_testcase {
  33      /**
  34       * Test user_get_user_details_courses
  35       */
  36      public function test_user_get_user_details_courses() {
  37          global $DB;
  38  
  39          $this->resetAfterTest();
  40  
  41          // Create user and modify user profile.
  42          $user1 = $this->getDataGenerator()->create_user();
  43          $user2 = $this->getDataGenerator()->create_user();
  44          $user3 = $this->getDataGenerator()->create_user();
  45  
  46          $course1 = $this->getDataGenerator()->create_course();
  47          $coursecontext = \context_course::instance($course1->id);
  48          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
  49          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
  50          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
  51          role_assign($teacherrole->id, $user1->id, $coursecontext->id);
  52          role_assign($teacherrole->id, $user2->id, $coursecontext->id);
  53  
  54          accesslib_clear_all_caches_for_unit_testing();
  55  
  56          // Get user2 details as a user with super system capabilities.
  57          $result = user_get_user_details_courses($user2);
  58          $this->assertEquals($user2->id, $result['id']);
  59          $this->assertEquals(fullname($user2), $result['fullname']);
  60          $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
  61  
  62          $this->setUser($user1);
  63          // Get user2 details as a user who can only see this user in a course.
  64          $result = user_get_user_details_courses($user2);
  65          $this->assertEquals($user2->id, $result['id']);
  66          $this->assertEquals(fullname($user2), $result['fullname']);
  67          $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
  68  
  69          // Get user2 details as a user who doesn't share any course with user2.
  70          $this->setUser($user3);
  71          $result = user_get_user_details_courses($user2);
  72          $this->assertNull($result);
  73      }
  74  
  75      /**
  76       * Verify return when course groupmode set to 'no groups'.
  77       */
  78      public function test_user_get_user_details_courses_groupmode_nogroups() {
  79          $this->resetAfterTest();
  80  
  81          // Enrol 2 users into a course with groupmode set to 'no groups'.
  82          // Profiles should be visible.
  83          $user1 = $this->getDataGenerator()->create_user();
  84          $user2 = $this->getDataGenerator()->create_user();
  85          $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 0]);
  86          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
  87          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
  88  
  89          $this->setUser($user1);
  90          $userdetails = user_get_user_details_courses($user2);
  91          $this->assertIsArray($userdetails);
  92          $this->assertEquals($user2->id, $userdetails['id']);
  93      }
  94  
  95      /**
  96       * Verify return when course groupmode set to 'separate groups'.
  97       */
  98      public function test_user_get_user_details_courses_groupmode_separate() {
  99          $this->resetAfterTest();
 100  
 101          // Enrol 2 users into a course with groupmode set to 'separate groups'.
 102          // The users are not in any groups, so profiles should be hidden (same as if they were in separate groups).
 103          $user1 = $this->getDataGenerator()->create_user();
 104          $user2 = $this->getDataGenerator()->create_user();
 105          $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 1]);
 106          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
 107          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
 108  
 109          $this->setUser($user1);
 110          $this->assertNull(user_get_user_details_courses($user2));
 111      }
 112  
 113      /**
 114       * Verify return when course groupmode set to 'visible groups'.
 115       */
 116      public function test_user_get_user_details_courses_groupmode_visible() {
 117          $this->resetAfterTest();
 118  
 119          // Enrol 2 users into a course with groupmode set to 'visible groups'.
 120          // The users are not in any groups, and profiles should be visible because of the groupmode.
 121          $user1 = $this->getDataGenerator()->create_user();
 122          $user2 = $this->getDataGenerator()->create_user();
 123          $course = $this->getDataGenerator()->create_course((object) ['groupmode' => 2]);
 124          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
 125          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
 126  
 127          $this->setUser($user1);
 128          $userdetails = user_get_user_details_courses($user2);
 129          $this->assertIsArray($userdetails);
 130          $this->assertEquals($user2->id, $userdetails['id']);
 131      }
 132  
 133      /**
 134       * Tests that the user fields returned by the method can be limited.
 135       *
 136       * @covers ::user_get_user_details_courses
 137       */
 138      public function test_user_get_user_details_courses_limit_return() {
 139          $this->resetAfterTest();
 140  
 141          // Setup some data.
 142          $user1 = $this->getDataGenerator()->create_user();
 143          $user2 = $this->getDataGenerator()->create_user();
 144          $course = $this->getDataGenerator()->create_course();
 145          $this->getDataGenerator()->enrol_user($user1->id, $course->id);
 146          $this->getDataGenerator()->enrol_user($user2->id, $course->id);
 147  
 148          // Calculate the minimum fields that can be returned.
 149          $namefields = \core_user\fields::for_name()->get_required_fields();
 150          $fields = array_intersect($namefields, user_get_default_fields());
 151  
 152          $minimaluser = (object) [
 153              'id' => $user2->id,
 154              'deleted' => $user2->deleted,
 155          ];
 156  
 157          foreach ($namefields as $field) {
 158              $minimaluser->$field = $user2->$field;
 159          }
 160  
 161          $this->setUser($user1);
 162          $fulldetails = user_get_user_details_courses($user2);
 163          $limiteddetails = user_get_user_details_courses($minimaluser, $fields);
 164          $this->assertIsArray($fulldetails);
 165          $this->assertIsArray($limiteddetails);
 166          $this->assertEquals($user2->id, $fulldetails['id']);
 167          $this->assertEquals($user2->id, $limiteddetails['id']);
 168  
 169          // Test that less data was returned when using a filter.
 170          $fullcount = count($fulldetails);
 171          $limitedcount = count($limiteddetails);
 172          $this->assertLessThan($fullcount, $limitedcount);
 173          $this->assertNotEquals($fulldetails, $limiteddetails);
 174      }
 175  
 176      /**
 177       * Test user_update_user.
 178       */
 179      public function test_user_update_user() {
 180          global $DB;
 181  
 182          $this->resetAfterTest();
 183  
 184          // Create user and modify user profile.
 185          $user = $this->getDataGenerator()->create_user();
 186          $user->firstname = 'Test';
 187          $user->password = 'M00dLe@T';
 188  
 189          // Update user and capture event.
 190          $sink = $this->redirectEvents();
 191          user_update_user($user);
 192          $events = $sink->get_events();
 193          $sink->close();
 194          $event = array_pop($events);
 195  
 196          // Test updated value.
 197          $dbuser = $DB->get_record('user', array('id' => $user->id));
 198          $this->assertSame($user->firstname, $dbuser->firstname);
 199          $this->assertNotSame('M00dLe@T', $dbuser->password);
 200  
 201          // Test event.
 202          $this->assertInstanceOf('\core\event\user_updated', $event);
 203          $this->assertSame($user->id, $event->objectid);
 204          $this->assertEquals(\context_user::instance($user->id), $event->get_context());
 205  
 206          // Update user with no password update.
 207          $password = $user->password = hash_internal_user_password('M00dLe@T');
 208          user_update_user($user, false);
 209          $dbuser = $DB->get_record('user', array('id' => $user->id));
 210          $this->assertSame($password, $dbuser->password);
 211  
 212          // Verify event is not triggred by user_update_user when needed.
 213          $sink = $this->redirectEvents();
 214          user_update_user($user, false, false);
 215          $events = $sink->get_events();
 216          $sink->close();
 217          $this->assertCount(0, $events);
 218  
 219          // With password, there should be 1 event.
 220          $sink = $this->redirectEvents();
 221          user_update_user($user, true, false);
 222          $events = $sink->get_events();
 223          $sink->close();
 224          $this->assertCount(1, $events);
 225          $event = array_pop($events);
 226          $this->assertInstanceOf('\core\event\user_password_updated', $event);
 227  
 228          // Test user data validation.
 229          $user->username = 'johndoe123';
 230          $user->auth = 'shibolth';
 231          $user->country = 'WW';
 232          $user->lang = 'xy';
 233          $user->theme = 'somewrongthemename';
 234          $user->timezone = '30.5';
 235          $debugmessages = $this->getDebuggingMessages();
 236          user_update_user($user, true, false);
 237          $this->assertDebuggingCalledCount(5, $debugmessages);
 238  
 239          // Now, with valid user data.
 240          $user->username = 'johndoe321';
 241          $user->auth = 'shibboleth';
 242          $user->country = 'AU';
 243          $user->lang = 'en';
 244          $user->theme = 'classic';
 245          $user->timezone = 'Australia/Perth';
 246          user_update_user($user, true, false);
 247          $this->assertDebuggingNotCalled();
 248      }
 249  
 250      /**
 251       * Test create_users.
 252       */
 253      public function test_create_users() {
 254          global $DB;
 255  
 256          $this->resetAfterTest();
 257  
 258          $user = array(
 259              'username' => 'usernametest1',
 260              'password' => 'Moodle2012!',
 261              'idnumber' => 'idnumbertest1',
 262              'firstname' => 'First Name User Test 1',
 263              'lastname' => 'Last Name User Test 1',
 264              'middlename' => 'Middle Name User Test 1',
 265              'lastnamephonetic' => '最後のお名前のテスト一号',
 266              'firstnamephonetic' => 'お名前のテスト一号',
 267              'alternatename' => 'Alternate Name User Test 1',
 268              'email' => 'usertest1@example.com',
 269              'description' => 'This is a description for user 1',
 270              'city' => 'Perth',
 271              'country' => 'AU'
 272              );
 273  
 274          // Create user and capture event.
 275          $sink = $this->redirectEvents();
 276          $user['id'] = user_create_user($user);
 277          $events = $sink->get_events();
 278          $sink->close();
 279          $event = array_pop($events);
 280  
 281          // Test user info in DB.
 282          $dbuser = $DB->get_record('user', array('id' => $user['id']));
 283          $this->assertEquals($dbuser->username, $user['username']);
 284          $this->assertEquals($dbuser->idnumber, $user['idnumber']);
 285          $this->assertEquals($dbuser->firstname, $user['firstname']);
 286          $this->assertEquals($dbuser->lastname, $user['lastname']);
 287          $this->assertEquals($dbuser->email, $user['email']);
 288          $this->assertEquals($dbuser->description, $user['description']);
 289          $this->assertEquals($dbuser->city, $user['city']);
 290          $this->assertEquals($dbuser->country, $user['country']);
 291  
 292          // Test event.
 293          $this->assertInstanceOf('\core\event\user_created', $event);
 294          $this->assertEquals($user['id'], $event->objectid);
 295          $this->assertEquals(\context_user::instance($user['id']), $event->get_context());
 296  
 297          // Verify event is not triggred by user_create_user when needed.
 298          $user = array('username' => 'usernametest2'); // Create another user.
 299          $sink = $this->redirectEvents();
 300          user_create_user($user, true, false);
 301          $events = $sink->get_events();
 302          $sink->close();
 303          $this->assertCount(0, $events);
 304  
 305          // Test user data validation, first some invalid data.
 306          $user['username'] = 'johndoe123';
 307          $user['auth'] = 'shibolth';
 308          $user['country'] = 'WW';
 309          $user['lang'] = 'xy';
 310          $user['theme'] = 'somewrongthemename';
 311          $user['timezone'] = '-30.5';
 312          $debugmessages = $this->getDebuggingMessages();
 313          $user['id'] = user_create_user($user, true, false);
 314          $this->assertDebuggingCalledCount(5, $debugmessages);
 315          $dbuser = $DB->get_record('user', array('id' => $user['id']));
 316          $this->assertEquals($dbuser->country, 0);
 317          $this->assertEquals($dbuser->lang, 'en');
 318          $this->assertEquals($dbuser->timezone, '');
 319  
 320          // Now, with valid user data.
 321          $user['username'] = 'johndoe321';
 322          $user['auth'] = 'shibboleth';
 323          $user['country'] = 'AU';
 324          $user['lang'] = 'en';
 325          $user['theme'] = 'classic';
 326          $user['timezone'] = 'Australia/Perth';
 327          user_create_user($user, true, false);
 328          $this->assertDebuggingNotCalled();
 329      }
 330  
 331      /**
 332       * Test that creating users populates default values
 333       *
 334       * @covers ::user_create_user
 335       */
 336      public function test_user_create_user_default_values(): void {
 337          global $CFG;
 338  
 339          $this->resetAfterTest();
 340  
 341          // Update default values for city/country (both initially empty).
 342          set_config('defaultcity', 'Nadi');
 343          set_config('country', 'FJ');
 344  
 345          $userid = user_create_user((object) [
 346              'username' => 'newuser',
 347          ], false, false);
 348  
 349          $user = \core_user::get_user($userid);
 350          $this->assertEquals($CFG->calendartype, $user->calendartype);
 351          $this->assertEquals($CFG->defaultpreference_maildisplay, $user->maildisplay);
 352          $this->assertEquals($CFG->defaultpreference_mailformat, $user->mailformat);
 353          $this->assertEquals($CFG->defaultpreference_maildigest, $user->maildigest);
 354          $this->assertEquals($CFG->defaultpreference_autosubscribe, $user->autosubscribe);
 355          $this->assertEquals($CFG->defaultpreference_trackforums, $user->trackforums);
 356          $this->assertEquals($CFG->lang, $user->lang);
 357          $this->assertEquals($CFG->defaultcity, $user->city);
 358          $this->assertEquals($CFG->country, $user->country);
 359      }
 360  
 361      /**
 362       * Test that {@link user_create_user()} throws exception when invalid username is provided.
 363       *
 364       * @dataProvider data_create_user_invalid_username
 365       * @param string $username Invalid username
 366       * @param string $expectmessage Expected exception message
 367       */
 368      public function test_create_user_invalid_username($username, $expectmessage) {
 369          global $CFG;
 370  
 371          $this->resetAfterTest();
 372          $CFG->extendedusernamechars = false;
 373  
 374          $user = [
 375              'username' => $username,
 376          ];
 377  
 378          $this->expectException('moodle_exception');
 379          $this->expectExceptionMessage($expectmessage);
 380  
 381          user_create_user($user);
 382      }
 383  
 384      /**
 385       * Data provider for {@link self::test_create_user_invalid_username()}.
 386       *
 387       * @return array
 388       */
 389      public function data_create_user_invalid_username() {
 390          return [
 391              'empty_string' => [
 392                  '',
 393                  'The username cannot be blank',
 394              ],
 395              'only_whitespace' => [
 396                  "\t\t  \t\n ",
 397                  'The username cannot be blank',
 398              ],
 399              'lower_case' => [
 400                  'Mudrd8mz',
 401                  'The username must be in lower case',
 402              ],
 403              'extended_chars' => [
 404                  'dmudrák',
 405                  'The given username contains invalid characters',
 406              ],
 407          ];
 408      }
 409  
 410      /**
 411       * Test function user_count_login_failures().
 412       */
 413      public function test_user_count_login_failures() {
 414          $this->resetAfterTest();
 415          $user = $this->getDataGenerator()->create_user();
 416          $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
 417          for ($i = 0; $i < 10; $i++) {
 418              login_attempt_failed($user);
 419          }
 420          $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
 421          $count = user_count_login_failures($user); // Reset count.
 422          $this->assertEquals(10, $count);
 423          $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
 424  
 425          for ($i = 0; $i < 10; $i++) {
 426              login_attempt_failed($user);
 427          }
 428          $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
 429          $count = user_count_login_failures($user, false); // Do not reset count.
 430          $this->assertEquals(10, $count);
 431          $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
 432      }
 433  
 434      /**
 435       * Test function user_add_password_history().
 436       */
 437      public function test_user_add_password_history() {
 438          global $DB;
 439  
 440          $this->resetAfterTest();
 441  
 442          $user1 = $this->getDataGenerator()->create_user();
 443          $user2 = $this->getDataGenerator()->create_user();
 444          $user3 = $this->getDataGenerator()->create_user();
 445          $DB->delete_records('user_password_history', array());
 446  
 447          set_config('passwordreuselimit', 0);
 448  
 449          user_add_password_history($user1->id, 'pokus');
 450          $this->assertEquals(0, $DB->count_records('user_password_history'));
 451  
 452          // Test adding and discarding of old.
 453  
 454          set_config('passwordreuselimit', 3);
 455  
 456          user_add_password_history($user1->id, 'pokus');
 457          $this->assertEquals(1, $DB->count_records('user_password_history'));
 458          $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 459  
 460          user_add_password_history($user1->id, 'pokus2');
 461          user_add_password_history($user1->id, 'pokus3');
 462          user_add_password_history($user1->id, 'pokus4');
 463          $this->assertEquals(3, $DB->count_records('user_password_history'));
 464          $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 465  
 466          user_add_password_history($user2->id, 'pokus1');
 467          $this->assertEquals(4, $DB->count_records('user_password_history'));
 468          $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 469          $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 470  
 471          user_add_password_history($user2->id, 'pokus2');
 472          user_add_password_history($user2->id, 'pokus3');
 473          $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 474  
 475          $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 476          user_add_password_history($user2->id, 'pokus4');
 477          $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 478          $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 479  
 480          $removed = array_shift($ids);
 481          $added = array_pop($newids);
 482          $this->assertSame($ids, $newids);
 483          $this->assertGreaterThan($removed, $added);
 484  
 485          // Test disabling prevents changes.
 486  
 487          set_config('passwordreuselimit', 0);
 488  
 489          $this->assertEquals(6, $DB->count_records('user_password_history'));
 490  
 491          $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 492          user_add_password_history($user2->id, 'pokus5');
 493          user_add_password_history($user3->id, 'pokus1');
 494          $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 495          $this->assertSame($ids, $newids);
 496          $this->assertEquals(6, $DB->count_records('user_password_history'));
 497  
 498          set_config('passwordreuselimit', -1);
 499  
 500          $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 501          user_add_password_history($user2->id, 'pokus6');
 502          user_add_password_history($user3->id, 'pokus6');
 503          $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
 504          $this->assertSame($ids, $newids);
 505          $this->assertEquals(6, $DB->count_records('user_password_history'));
 506      }
 507  
 508      /**
 509       * Test function user_add_password_history().
 510       */
 511      public function test_user_is_previously_used_password() {
 512          global $DB;
 513  
 514          $this->resetAfterTest();
 515  
 516          $user1 = $this->getDataGenerator()->create_user();
 517          $user2 = $this->getDataGenerator()->create_user();
 518          $DB->delete_records('user_password_history', array());
 519  
 520          set_config('passwordreuselimit', 0);
 521  
 522          user_add_password_history($user1->id, 'pokus');
 523          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus'));
 524  
 525          set_config('passwordreuselimit', 3);
 526  
 527          user_add_password_history($user2->id, 'pokus1');
 528          user_add_password_history($user2->id, 'pokus2');
 529  
 530          user_add_password_history($user1->id, 'pokus1');
 531          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
 532          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
 533          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
 534          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
 535  
 536          user_add_password_history($user1->id, 'pokus2');
 537          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
 538          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
 539          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
 540          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
 541  
 542          user_add_password_history($user1->id, 'pokus3');
 543          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
 544          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
 545          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
 546          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
 547  
 548          user_add_password_history($user1->id, 'pokus4');
 549          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
 550          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
 551          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
 552          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
 553  
 554          set_config('passwordreuselimit', 2);
 555  
 556          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
 557          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
 558          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
 559          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
 560  
 561          set_config('passwordreuselimit', 3);
 562  
 563          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
 564          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
 565          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
 566          $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
 567  
 568          set_config('passwordreuselimit', 0);
 569  
 570          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
 571          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
 572          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
 573          $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
 574      }
 575  
 576      /**
 577       * Test that password history is deleted together with user.
 578       */
 579      public function test_delete_of_hashes_on_user_delete() {
 580          global $DB;
 581  
 582          $this->resetAfterTest();
 583  
 584          $user1 = $this->getDataGenerator()->create_user();
 585          $user2 = $this->getDataGenerator()->create_user();
 586          $DB->delete_records('user_password_history', array());
 587  
 588          set_config('passwordreuselimit', 3);
 589  
 590          user_add_password_history($user1->id, 'pokus');
 591          user_add_password_history($user2->id, 'pokus1');
 592          user_add_password_history($user2->id, 'pokus2');
 593  
 594          $this->assertEquals(3, $DB->count_records('user_password_history'));
 595          $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 596          $this->assertEquals(2, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 597  
 598          delete_user($user2);
 599          $this->assertEquals(1, $DB->count_records('user_password_history'));
 600          $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
 601          $this->assertEquals(0, $DB->count_records('user_password_history', array('userid' => $user2->id)));
 602      }
 603  
 604      /**
 605       * Test user_list_view function
 606       */
 607      public function test_user_list_view() {
 608  
 609          $this->resetAfterTest();
 610  
 611          // Course without sections.
 612          $course = $this->getDataGenerator()->create_course();
 613          $context = \context_course::instance($course->id);
 614  
 615          $this->setAdminUser();
 616  
 617          // Redirect events to the sink, so we can recover them later.
 618          $sink = $this->redirectEvents();
 619  
 620          user_list_view($course, $context);
 621          $events = $sink->get_events();
 622          $this->assertCount(1, $events);
 623          $event = reset($events);
 624  
 625          // Check the event details are correct.
 626          $this->assertInstanceOf('\core\event\user_list_viewed', $event);
 627          $this->assertEquals($context, $event->get_context());
 628          $this->assertEquals($course->shortname, $event->other['courseshortname']);
 629          $this->assertEquals($course->fullname, $event->other['coursefullname']);
 630  
 631      }
 632  
 633      /**
 634       * Test setting the user menu avatar size.
 635       */
 636      public function test_user_menu_custom_avatar_size() {
 637          global $PAGE;
 638          $this->resetAfterTest(true);
 639  
 640          $testsize = 100;
 641  
 642          $PAGE->set_url('/');
 643          $user = $this->getDataGenerator()->create_user();
 644          $this->setUser($user);
 645          $opts = user_get_user_navigation_info($user, $PAGE, array('avatarsize' => $testsize));
 646          $avatarhtml = $opts->metadata['useravatar'];
 647  
 648          $matches = [];
 649          preg_match('/size-100/', $avatarhtml, $matches);
 650          $this->assertCount(1, $matches);
 651      }
 652  
 653      /**
 654       * Test user_can_view_profile
 655       */
 656      public function test_user_can_view_profile() {
 657          global $DB, $CFG;
 658  
 659          $this->resetAfterTest();
 660  
 661          // Create five users.
 662          $user1 = $this->getDataGenerator()->create_user();
 663          $user2 = $this->getDataGenerator()->create_user();
 664          $user3 = $this->getDataGenerator()->create_user();
 665          $user4 = $this->getDataGenerator()->create_user();
 666          $user5 = $this->getDataGenerator()->create_user();
 667          $user6 = $this->getDataGenerator()->create_user(array('deleted' => 1));
 668          $user7 = $this->getDataGenerator()->create_user();
 669          $user8 = $this->getDataGenerator()->create_user();
 670          $user8->id = 0; // Visitor.
 671  
 672          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 673          // Add the course creator role to the course contact and assign a user to that role.
 674          $CFG->coursecontact = '2';
 675          $coursecreatorrole = $DB->get_record('role', array('shortname' => 'coursecreator'));
 676          $this->getDataGenerator()->role_assign($coursecreatorrole->id, $user7->id);
 677  
 678           // Create two courses.
 679          $course1 = $this->getDataGenerator()->create_course();
 680          $course2 = $this->getDataGenerator()->create_course();
 681          $coursecontext = \context_course::instance($course2->id);
 682          // Prepare another course with separate groups and groupmodeforce set to true.
 683          $record = new \stdClass();
 684          $record->groupmode = 1;
 685          $record->groupmodeforce = 1;
 686          $course3 = $this->getDataGenerator()->create_course($record);
 687          // Enrol users 1 and 2 in first course.
 688          $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
 689          $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
 690          // Enrol users 2 and 3 in second course.
 691          $this->getDataGenerator()->enrol_user($user2->id, $course2->id);
 692          $this->getDataGenerator()->enrol_user($user3->id, $course2->id);
 693          // Enrol users 1, 4, and 5 into course 3.
 694          $this->getDataGenerator()->enrol_user($user1->id, $course3->id);
 695          $this->getDataGenerator()->enrol_user($user4->id, $course3->id);
 696          $this->getDataGenerator()->enrol_user($user5->id, $course3->id);
 697  
 698          // User 3 should not be able to see user 1, either by passing their own course (course 2) or user 1's course (course 1).
 699          $this->setUser($user3);
 700          $this->assertFalse(user_can_view_profile($user1, $course2));
 701          $this->assertFalse(user_can_view_profile($user1, $course1));
 702  
 703          // Remove capability moodle/user:viewdetails in course 2.
 704          assign_capability('moodle/user:viewdetails', CAP_PROHIBIT, $studentrole->id, $coursecontext);
 705          // Set current user to user 1.
 706          $this->setUser($user1);
 707          // User 1 can see User 1's profile.
 708          $this->assertTrue(user_can_view_profile($user1));
 709  
 710          $tempcfg = $CFG->forceloginforprofiles;
 711          $CFG->forceloginforprofiles = 0;
 712          // Not forced to log in to view profiles, should be able to see all profiles besides user 6.
 713          $users = array($user1, $user2, $user3, $user4, $user5, $user7);
 714          foreach ($users as $user) {
 715              $this->assertTrue(user_can_view_profile($user));
 716          }
 717          // Restore setting.
 718          $CFG->forceloginforprofiles = $tempcfg;
 719  
 720          // User 1 can not see user 6 as they have been deleted.
 721          $this->assertFalse(user_can_view_profile($user6));
 722          // User 1 can see User 7 as they are a course contact.
 723          $this->assertTrue(user_can_view_profile($user7));
 724          // User 1 is in a course with user 2 and has the right capability - return true.
 725          $this->assertTrue(user_can_view_profile($user2));
 726          // User 1 is not in a course with user 3 - return false.
 727          $this->assertFalse(user_can_view_profile($user3));
 728  
 729          // Set current user to user 2.
 730          $this->setUser($user2);
 731          // User 2 is in a course with user 3 but does not have the right capability - return false.
 732          $this->assertFalse(user_can_view_profile($user3));
 733  
 734          // Set user 1 in one group and users 4 and 5 in another group.
 735          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
 736          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
 737          groups_add_member($group1->id, $user1->id);
 738          groups_add_member($group2->id, $user4->id);
 739          groups_add_member($group2->id, $user5->id);
 740          $this->setUser($user1);
 741          // Check that user 1 can not see user 4.
 742          $this->assertFalse(user_can_view_profile($user4));
 743          // Check that user 5 can see user 4.
 744          $this->setUser($user5);
 745          $this->assertTrue(user_can_view_profile($user4));
 746  
 747          // Test the user:viewalldetails cap check using the course creator role which, by default, can't see student profiles.
 748          $this->setUser($user7);
 749          $this->assertFalse(user_can_view_profile($user4));
 750          assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $coursecreatorrole->id, \context_system::instance()->id, true);
 751          reload_all_capabilities();
 752          $this->assertTrue(user_can_view_profile($user4));
 753          unassign_capability('moodle/user:viewalldetails', $coursecreatorrole->id, $coursecontext->id);
 754          reload_all_capabilities();
 755  
 756          $CFG->coursecontact = null;
 757  
 758          // Visitor (Not a guest user, userid=0).
 759          $CFG->forceloginforprofiles = 1;
 760          $this->setUser($user8);
 761          $this->assertFalse(user_can_view_profile($user1));
 762  
 763          // Let us test with guest user.
 764          $this->setGuestUser();
 765          $CFG->forceloginforprofiles = 1;
 766          foreach ($users as $user) {
 767              $this->assertFalse(user_can_view_profile($user));
 768          }
 769  
 770          // Even with cap, still guests should not be allowed in.
 771          $guestrole = $DB->get_records_menu('role', array('shortname' => 'guest'), 'id', 'archetype, id');
 772          assign_capability('moodle/user:viewdetails', CAP_ALLOW, $guestrole['guest'], \context_system::instance()->id, true);
 773          reload_all_capabilities();
 774          foreach ($users as $user) {
 775              $this->assertFalse(user_can_view_profile($user));
 776          }
 777  
 778          $CFG->forceloginforprofiles = 0;
 779          foreach ($users as $user) {
 780              $this->assertTrue(user_can_view_profile($user));
 781          }
 782  
 783          // Let us test with Visitor user.
 784          $this->setUser($user8);
 785          $CFG->forceloginforprofiles = 1;
 786          foreach ($users as $user) {
 787              $this->assertFalse(user_can_view_profile($user));
 788          }
 789  
 790          $CFG->forceloginforprofiles = 0;
 791          foreach ($users as $user) {
 792              $this->assertTrue(user_can_view_profile($user));
 793          }
 794  
 795          // Testing non-shared courses where capabilities are met, using system role overrides.
 796          $CFG->forceloginforprofiles = $tempcfg;
 797          $course4 = $this->getDataGenerator()->create_course();
 798          $this->getDataGenerator()->enrol_user($user1->id, $course4->id);
 799  
 800          // Assign a manager role at the system context.
 801          $managerrole = $DB->get_record('role', array('shortname' => 'manager'));
 802          $user9 = $this->getDataGenerator()->create_user();
 803          $this->getDataGenerator()->role_assign($managerrole->id, $user9->id);
 804  
 805          // Make sure viewalldetails and viewdetails are overridden to 'prevent' (i.e. can be overridden at a lower context).
 806          $systemcontext = \context_system::instance();
 807          assign_capability('moodle/user:viewdetails', CAP_PREVENT, $managerrole->id, $systemcontext, true);
 808          assign_capability('moodle/user:viewalldetails', CAP_PREVENT, $managerrole->id, $systemcontext, true);
 809  
 810          // And override these to 'Allow' in a specific course.
 811          $course4context = \context_course::instance($course4->id);
 812          assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $managerrole->id, $course4context, true);
 813          assign_capability('moodle/user:viewdetails', CAP_ALLOW, $managerrole->id, $course4context, true);
 814  
 815          // The manager now shouldn't have viewdetails in the system or user context.
 816          $this->setUser($user9);
 817          $user1context = \context_user::instance($user1->id);
 818          $this->assertFalse(has_capability('moodle/user:viewdetails', $systemcontext));
 819          $this->assertFalse(has_capability('moodle/user:viewdetails', $user1context));
 820  
 821          // Confirm that user_can_view_profile() returns true for $user1 when called without $course param. It should find $course1.
 822          $this->assertTrue(user_can_view_profile($user1));
 823  
 824          // Confirm this also works when restricting scope to just that course.
 825          $this->assertTrue(user_can_view_profile($user1, $course4));
 826      }
 827  
 828      /**
 829       * Test user_get_user_details
 830       */
 831      public function test_user_get_user_details() {
 832          global $DB;
 833  
 834          $this->resetAfterTest();
 835  
 836          // Create user and modify user profile.
 837          $teacher = $this->getDataGenerator()->create_user();
 838          $student = $this->getDataGenerator()->create_user();
 839          $studentfullname = fullname($student);
 840  
 841          $course1 = $this->getDataGenerator()->create_course();
 842          $coursecontext = \context_course::instance($course1->id);
 843          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
 844          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 845          $this->getDataGenerator()->enrol_user($teacher->id, $course1->id);
 846          $this->getDataGenerator()->enrol_user($student->id, $course1->id);
 847          role_assign($teacherrole->id, $teacher->id, $coursecontext->id);
 848          role_assign($studentrole->id, $student->id, $coursecontext->id);
 849  
 850          accesslib_clear_all_caches_for_unit_testing();
 851  
 852          // Get student details as a user with super system capabilities.
 853          $result = user_get_user_details($student, $course1);
 854          $this->assertEquals($student->id, $result['id']);
 855          $this->assertEquals($studentfullname, $result['fullname']);
 856          $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
 857  
 858          $this->setUser($teacher);
 859          // Get student details as a user who can only see this user in a course.
 860          $result = user_get_user_details($student, $course1);
 861          $this->assertEquals($student->id, $result['id']);
 862          $this->assertEquals($studentfullname, $result['fullname']);
 863          $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
 864  
 865          // Get student details with required fields.
 866          $result = user_get_user_details($student, $course1, array('id', 'fullname'));
 867          $this->assertCount(2, $result);
 868          $this->assertEquals($student->id, $result['id']);
 869          $this->assertEquals($studentfullname, $result['fullname']);
 870  
 871          // Get exception for invalid required fields.
 872          $this->expectException('moodle_exception');
 873          $result = user_get_user_details($student, $course1, array('wrongrequiredfield'));
 874      }
 875  
 876      /**
 877       * Regression test for MDL-57840.
 878       *
 879       * Ensure the fields "auth, confirmed, idnumber, lang, theme, timezone and mailformat" are present when
 880       * calling user_get_user_details() function.
 881       */
 882      public function test_user_get_user_details_missing_fields() {
 883          global $CFG;
 884  
 885          $this->resetAfterTest(true);
 886          $this->setAdminUser(); // We need capabilities to view the data.
 887          $user = self::getDataGenerator()->create_user([
 888                                                            'auth'       => 'email',
 889                                                            'confirmed'  => '0',
 890                                                            'idnumber'   => 'someidnumber',
 891                                                            'lang'       => 'en',
 892                                                            'theme'      => $CFG->theme,
 893                                                            'timezone'   => '5',
 894                                                            'mailformat' => '0',
 895                                                        ]);
 896  
 897          // Fields that should get by default.
 898          $got = user_get_user_details($user);
 899          self::assertSame('email', $got['auth']);
 900          self::assertSame('0', $got['confirmed']);
 901          self::assertSame('someidnumber', $got['idnumber']);
 902          self::assertSame('en', $got['lang']);
 903          self::assertSame($CFG->theme, $got['theme']);
 904          self::assertSame('5', $got['timezone']);
 905          self::assertSame('0', $got['mailformat']);
 906      }
 907  
 908      /**
 909       * Test user_get_user_details_permissions.
 910       * @covers ::user_get_user_details
 911       */
 912      public function test_user_get_user_details_permissions() {
 913          global $CFG;
 914  
 915          $this->resetAfterTest();
 916  
 917          // Create user and modify user profile.
 918          $teacher = $this->getDataGenerator()->create_user();
 919          $student1 = $this->getDataGenerator()->create_user(['idnumber' => 'user1id', 'city' => 'Barcelona', 'address' => 'BCN 1B']);
 920          $student2 = $this->getDataGenerator()->create_user();
 921          $student1fullname = fullname($student1);
 922  
 923          $course = $this->getDataGenerator()->create_course();
 924          $coursecontext = \context_course::instance($course->id);
 925          $this->getDataGenerator()->enrol_user($teacher->id, $course->id);
 926          $this->getDataGenerator()->enrol_user($student1->id, $course->id);
 927          $this->getDataGenerator()->enrol_user($student2->id, $course->id);
 928          $this->getDataGenerator()->role_assign('teacher', $teacher->id, $coursecontext->id);
 929          $this->getDataGenerator()->role_assign('student', $student1->id, $coursecontext->id);
 930          $this->getDataGenerator()->role_assign('student', $student2->id, $coursecontext->id);
 931  
 932          accesslib_clear_all_caches_for_unit_testing();
 933  
 934          // Get student details as a user with super system capabilities.
 935          $result = user_get_user_details($student1, $course);
 936          $this->assertEquals($student1->id, $result['id']);
 937          $this->assertEquals($student1fullname, $result['fullname']);
 938          $this->assertEquals($course->id, $result['enrolledcourses'][0]['id']);
 939  
 940          $this->setUser($student2);
 941  
 942          // Get student details with required fields.
 943          $result = user_get_user_details($student1, $course, array('id', 'fullname', 'timezone', 'city', 'address', 'idnumber'));
 944          $this->assertCount(4, $result); // Ensure address (never returned), idnumber (identity field) are not returned here.
 945          $this->assertEquals($student1->id, $result['id']);
 946          $this->assertEquals($student1fullname, $result['fullname']);
 947          $this->assertEquals($student1->timezone, $result['timezone']);
 948          $this->assertEquals($student1->city, $result['city']);
 949  
 950          // Set new identity fields and hidden fields and try to retrieve them without permission.
 951          $CFG->showuseridentity = $CFG->showuseridentity . ',idnumber';
 952          $CFG->hiddenuserfields = 'city';
 953          $result = user_get_user_details($student1, $course, array('id', 'fullname', 'timezone', 'city', 'address', 'idnumber'));
 954          $this->assertCount(3, $result); // Ensure address, city and idnumber are not returned here.
 955          $this->assertEquals($student1->id, $result['id']);
 956          $this->assertEquals($student1fullname, $result['fullname']);
 957          $this->assertEquals($student1->timezone, $result['timezone']);
 958  
 959          // Now, teacher should have permission to see the idnumber and city fields.
 960          $this->setUser($teacher);
 961          $result = user_get_user_details($student1, $course, array('id', 'fullname', 'timezone', 'city', 'address', 'idnumber'));
 962          $this->assertCount(5, $result); // Ensure address is not returned here.
 963          $this->assertEquals($student1->id, $result['id']);
 964          $this->assertEquals($student1fullname, $result['fullname']);
 965          $this->assertEquals($student1->timezone, $result['timezone']);
 966          $this->assertEquals($student1->idnumber, $result['idnumber']);
 967          $this->assertEquals($student1->city, $result['city']);
 968  
 969          // And admins can see anything.
 970          $this->setAdminUser();
 971          $result = user_get_user_details($student1, $course, array('id', 'fullname', 'timezone', 'city', 'address', 'idnumber'));
 972          $this->assertCount(6, $result);
 973          $this->assertEquals($student1->id, $result['id']);
 974          $this->assertEquals($student1fullname, $result['fullname']);
 975          $this->assertEquals($student1->timezone, $result['timezone']);
 976          $this->assertEquals($student1->idnumber, $result['idnumber']);
 977          $this->assertEquals($student1->city, $result['city']);
 978          $this->assertEquals($student1->address, $result['address']);
 979      }
 980  
 981      /**
 982       * Test user_get_user_details_groups.
 983       * @covers ::user_get_user_details
 984       */
 985      public function test_user_get_user_details_groups() {
 986          $this->resetAfterTest();
 987  
 988          // Create user and modify user profile.
 989          $teacher = $this->getDataGenerator()->create_user();
 990          $student1 = $this->getDataGenerator()->create_user(['idnumber' => 'user1id', 'city' => 'Barcelona', 'address' => 'BCN 1B']);
 991          $student2 = $this->getDataGenerator()->create_user();
 992  
 993          $course = $this->getDataGenerator()->create_course();
 994          $coursecontext = \context_course::instance($course->id);
 995          $this->getDataGenerator()->enrol_user($teacher->id, $course->id);
 996          $this->getDataGenerator()->enrol_user($student1->id, $course->id);
 997          $this->getDataGenerator()->enrol_user($student2->id, $course->id);
 998          $this->getDataGenerator()->role_assign('teacher', $teacher->id, $coursecontext->id);
 999          $this->getDataGenerator()->role_assign('student', $student1->id, $coursecontext->id);
1000          $this->getDataGenerator()->role_assign('student', $student2->id, $coursecontext->id);
1001  
1002          $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => 'G1']);
1003          $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => 'G2']);
1004  
1005          // Each student in one group but teacher in two.
1006          groups_add_member($group1->id, $student1->id);
1007          groups_add_member($group1->id, $teacher->id);
1008          groups_add_member($group2->id, $student2->id);
1009          groups_add_member($group2->id, $teacher->id);
1010  
1011          accesslib_clear_all_caches_for_unit_testing();
1012  
1013          // A student can see other users groups when separate groups are not forced.
1014          $this->setUser($student2);
1015  
1016          // Get student details with groups.
1017          $result = user_get_user_details($student1, $course, array('id', 'fullname', 'groups'));
1018          $this->assertCount(3, $result);
1019          $this->assertEquals($group1->id, $result['groups'][0]['id']);
1020  
1021          // Teacher is in two different groups.
1022          $result = user_get_user_details($teacher, $course, array('id', 'fullname', 'groups'));
1023  
1024          // Order by group id.
1025          usort($result['groups'], function($a, $b) {
1026              return $a['id'] - $b['id'];
1027          });
1028  
1029          $this->assertCount(3, $result);
1030          $this->assertCount(2, $result['groups']);
1031          $this->assertEquals($group1->id, $result['groups'][0]['id']);
1032          $this->assertEquals($group2->id, $result['groups'][1]['id']);
1033  
1034          // Change to separate groups.
1035          $course->groupmode = SEPARATEGROUPS;
1036          $course->groupmodeforce = true;
1037          update_course($course);
1038  
1039          // Teacher is in two groups but I can only see the one shared with me.
1040          $result = user_get_user_details($teacher, $course, array('id', 'fullname', 'groups'));
1041  
1042          $this->assertCount(3, $result);
1043          $this->assertCount(1, $result['groups']);
1044          $this->assertEquals($group2->id, $result['groups'][0]['id']);
1045      }
1046  }