AutorÃa | Ultima modificación | Ver Log |
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\session;
use core\context\course as context_course;
use core\context\system as context_system;
/**
* Unit tests for loginas_helper class.
*
* @package core
* @author Jason den Dulk <jasondendulk@catalyst-au.net>
* @covers \core\session\loginas_helper
* @copyright 2025 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class loginas_helper_test extends \advanced_testcase {
/**
* Tests various users wanting to login as other users of the same role.
*/
public function test_loginas_same_role(): void {
global $CFG, $DB;
$this->resetAfterTest();
$managerrole = $DB->get_field('role', 'id', ['shortname' => 'manager']);
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
// By default, users cannot login as other users.
$this->assertNull(loginas_helper::get_context_user_can_login_as($user1, $user2));
$this->assertNull(loginas_helper::get_context_user_can_login_as($user2, $user1));
// Admins can login as other admins.
$originalsiteadmins = $CFG->siteadmins;
$CFG->siteadmins .= ',' . $user1->id . ',' . $user2->id;
$systemcontext = context_system::instance();
$this->assertEquals($systemcontext, loginas_helper::get_context_user_can_login_as($user1, $user2));
$this->assertEquals($systemcontext, loginas_helper::get_context_user_can_login_as($user2, $user1));
// Managers can login as other managers.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
role_assign($managerrole, $user1->id, $systemcontext->id);
role_assign($managerrole, $user2->id, $systemcontext->id);
$this->assertEquals($systemcontext, loginas_helper::get_context_user_can_login_as($user1, $user2));
$this->assertEquals($systemcontext, loginas_helper::get_context_user_can_login_as($user2, $user1));
// Course managers can login as other course managers, but only in the course context.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$coursecontext = context_course::instance($course->id);
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'manager');
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'manager');
$this->assertNull(loginas_helper::get_context_user_can_login_as($user1, $user2));
$this->assertNull(loginas_helper::get_context_user_can_login_as($user2, $user1));
$this->assertEquals($coursecontext, loginas_helper::get_context_user_can_login_as($user1, $user2, $course));
$this->assertEquals($coursecontext, loginas_helper::get_context_user_can_login_as($user2, $user1, $course));
// Students cannot login as another student.
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
$this->assertNull(loginas_helper::get_context_user_can_login_as($user1, $user2));
$this->assertNull(loginas_helper::get_context_user_can_login_as($user2, $user1));
$this->assertNull(loginas_helper::get_context_user_can_login_as($user1, $user2, $course));
$this->assertNull(loginas_helper::get_context_user_can_login_as($user2, $user1, $course));
}
/**
* Tests trying to login as a deleted user.
*/
public function test_loginas_deleted_user(): void {
global $USER;
$this->resetAfterTest();
$this->setAdminUser();
// Sanity check that ordinary login as works.
$user = $this->getDataGenerator()->create_user();
$this->assertEquals(context_system::instance(), loginas_helper::get_context_user_can_login_as($USER, $user));
// Cannot login as a user that has been deleted.
$user = $this->getDataGenerator()->create_user(['deleted' => true]);
$this->assertNull(loginas_helper::get_context_user_can_login_as($USER, $user));
}
/**
* Tests various users wanting to login as other users of differing roles.
*/
public function test_loginas_different_roles(): void {
global $CFG, $DB;
$this->resetAfterTest();
$systemcontext = context_system::instance();
$managerrole = $DB->get_field('role', 'id', ['shortname' => 'manager']);
$originalsiteadmins = $CFG->siteadmins;
$users = [];
for ($i = 0; $i < 11; ++$i) {
$users[] = $this->getDataGenerator()->create_user();
}
$courses = [
$this->getDataGenerator()->create_course(),
$this->getDataGenerator()->create_course(),
];
$coursecontexts = [
context_course::instance($courses[0]->id),
context_course::instance($courses[1]->id),
];
// User 0 is an admin.
$CFG->siteadmins .= ',' . $users[0]->id;
// User 1 is a manager.
role_assign($managerrole, $users[1]->id, $systemcontext->id);
// User 2 is a manager and an admin.
$CFG->siteadmins .= ',' . $users[2]->id;
role_assign($managerrole, $users[2]->id, $systemcontext->id);
// User 3 is a course manager for course 0.
$this->getDataGenerator()->enrol_user($users[3]->id, $courses[0]->id, 'manager');
// User 4 is a course manager for course 0 and a site manager.
$this->getDataGenerator()->enrol_user($users[4]->id, $courses[0]->id, 'manager');
role_assign($managerrole, $users[4]->id, $systemcontext->id);
// User 5 is a course manager for course 0 and an admin.
$this->getDataGenerator()->enrol_user($users[5]->id, $courses[0]->id, 'manager');
$CFG->siteadmins .= ',' . $users[5]->id;
// User 6 is a student for course 0.
$this->getDataGenerator()->enrol_user($users[6]->id, $courses[0]->id, 'student');
// User 7 is a student for course 0 and an admin.
$this->getDataGenerator()->enrol_user($users[7]->id, $courses[0]->id, 'student');
$CFG->siteadmins .= ',' . $users[7]->id;
// User 8 is a course manager for course 1.
$this->getDataGenerator()->enrol_user($users[8]->id, $courses[1]->id, 'manager');
// User 9 is a student for course 1.
$this->getDataGenerator()->enrol_user($users[9]->id, $courses[1]->id, 'student');
// User 10 is a user without courses or roles.
// This matrix defines loginas expectations. 'S' = system context. # = course context. 'X' = nothing.
$matrix = [
// ... 0 1 2 3 4 5 6 7 8 9 10.
0 => ['X', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'],
1 => ['X', 'X', 'X', 'S', 'S', 'X', 'S', 'X', 'S', 'S', 'S'],
2 => ['S', 'S', 'X', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'],
3 => ['X', 'X', 'X', 'X', 'X', 'X', '0', 'X', 'X', 'X', 'X'],
4 => ['X', 'S', 'X', 'S', 'X', 'X', 'S', 'X', 'S', 'S', 'S'],
5 => ['S', 'S', 'S', 'S', 'S', 'X', 'S', 'S', 'S', 'S', 'S'],
6 => ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'],
7 => ['S', 'S', 'S', 'S', 'S', 'S', 'S', 'X', 'S', 'S', 'S'],
8 => ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', '1', 'X'],
9 => ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'],
10 => ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'],
];
// Now test each user against each other user, and compare the results to the expectation matrix.
foreach ($users as $uid => $user) {
foreach ($users as $oid => $other) {
$resultsnocourse = loginas_helper::get_context_user_can_login_as($user, $other);
$resultscourse0 = loginas_helper::get_context_user_can_login_as($user, $other, $courses[0]);
$resultscourse1 = loginas_helper::get_context_user_can_login_as($user, $other, $courses[1]);
switch ($matrix[$uid][$oid]) {
case 'X':
// No loginas is possible.
$this->assertNull($resultsnocourse);
$this->assertNull($resultscourse0);
$this->assertNull($resultscourse1);
break;
case 'S':
// Loginas can happen at the site level.
$this->assertEquals($systemcontext, $resultsnocourse);
$this->assertEquals($systemcontext, $resultscourse0);
$this->assertEquals($systemcontext, $resultscourse1);
break;
case '0':
// Loginas can only happen within the context of course 0.
$this->assertNull($resultsnocourse);
$this->assertEquals($coursecontexts[0], $resultscourse0);
$this->assertNull($resultscourse1);
break;
case '1':
// Loginas can only happen within the context of course 1.
$this->assertNull($resultsnocourse);
$this->assertNull($resultscourse0);
$this->assertEquals($coursecontexts[1], $resultscourse1);
break;
}
}
}
}
/**
* Providor function for test_loginas_groups().
*
* @return array[]
*/
public static function loginas_groups_providor(): array {
return [
'Separate groups' => [
'groupmode' => SEPARATEGROUPS,
'accessallgroups' => true,
'canloginassamegroup' => true,
'canloginasdifferentgroup' => true,
],
'Separate groups, no access' => [
'groupmode' => SEPARATEGROUPS,
'accessallgroups' => false,
'canloginassamegroup' => true,
'canloginasdifferentgroup' => false,
],
'Visible groups' => [
'groupmode' => VISIBLEGROUPS,
'accessallgroups' => true,
'canloginassamegroup' => true,
'canloginasdifferentgroup' => true,
],
'Visible groups, no access' => [
'groupmode' => VISIBLEGROUPS,
'accessallgroups' => false,
'canloginassamegroup' => true,
'canloginasdifferentgroup' => true,
],
];
}
/**
* Tests users wanting to login as other users of different groups.
*
* @param int $groupmode
* @param bool $accessallgroups
* @param bool $canloginassamegroup
* @param bool $canloginasdifferentgroup
*
* @dataProvider loginas_groups_providor
*/
public function test_loginas_groups(
int $groupmode,
bool $accessallgroups,
bool $canloginassamegroup,
bool $canloginasdifferentgroup
): void {
global $DB;
$this->resetAfterTest();
// Set up manager and students.
$manager = $this->getDataGenerator()->create_user();
$student1 = $this->getDataGenerator()->create_user();
$student2 = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course(['groupmode' => $groupmode]);
$coursecontext = context_course::instance($course->id);
// Add or remove accessallgroups permission.
$managerroleid = $DB->get_field('role', 'id', ['shortname' => 'manager'], MUST_EXIST);
$permission = $accessallgroups ? CAP_ALLOW : CAP_PREVENT;
assign_capability('moodle/site:accessallgroups', $permission, $managerroleid, $coursecontext, true);
$this->getDataGenerator()->enrol_user($manager->id, $course->id, 'manager');
$this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student');
$this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student');
// Manager and student 1 are in the same group. Student 2 is in a different group.
$group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
$group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
$this->getDataGenerator()->create_group_member(['groupid' => $group1->id, 'userid' => $manager->id]);
$this->getDataGenerator()->create_group_member(['groupid' => $group1->id, 'userid' => $student1->id]);
$this->getDataGenerator()->create_group_member(['groupid' => $group2->id, 'userid' => $student2->id]);
// Manager wants to login as a student in the same group.
$this->assertEquals(
$canloginassamegroup,
(bool) loginas_helper::get_context_user_can_login_as($manager, $student1, $course)
);
// Manager wants to login as a student in a different group.
$this->assertEquals(
$canloginasdifferentgroup,
(bool) loginas_helper::get_context_user_can_login_as($manager, $student2, $course)
);
}
}