c8e062133ec90be2658378e8aeabedcbb62de119
[moodle.git] / lib / simpletest / testaccesslib.php
1 <?php
2 /**
3  * Unit tests for (some of) ../accesslib.php.
4  *
5  * @copyright &copy; 2006 The Open University
6  * @author T.J.Hunt@open.ac.uk
7  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
8  * @package moodlecore
9  */
11 if (!defined('MOODLE_INTERNAL')) {
12     die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
13 }
15 class accesslib_test extends UnitTestCaseUsingDatabase {
17     public static $includecoverage = array('lib/accesslib.php');
19     function test_get_parent_contexts() {
20         $context = get_context_instance(CONTEXT_SYSTEM);
21         $this->assertEqual(get_parent_contexts($context), array());
23         $context = new stdClass;
24         $context->path = '/1/25';
25         $this->assertEqual(get_parent_contexts($context), array(1));
27         $context = new stdClass;
28         $context->path = '/1/123/234/345/456';
29         $this->assertEqual(get_parent_contexts($context), array(345, 234, 123, 1));
30     }
32     function test_get_parent_contextid() {
33         $context = get_context_instance(CONTEXT_SYSTEM);
34         $this->assertFalse(get_parent_contextid($context));
36         $context = new stdClass;
37         $context->path = '/1/25';
38         $this->assertEqual(get_parent_contextid($context), 1);
40         $context = new stdClass;
41         $context->path = '/1/123/234/345/456';
42         $this->assertEqual(get_parent_contextid($context), 345);
43     }
45     function test_get_users_by_capability() {
46         global $CFG;
48         $tablenames = array('capabilities', 'context', 'role', 'role_capabilities',
49                 'role_allow_assign', 'role_allow_override', 'role_assignments', 'role_context_levels',
50                 'user', 'groups_members', 'cache_flags', 'events_handlers', 'user_lastaccess', 'course');
51         $this->create_test_tables($tablenames, 'lib');
53         accesslib_clear_all_caches_for_unit_testing();
54         $this->switch_to_test_db();
55         $this->switch_to_test_cfg();
57         $course = new stdClass();
58         $course->category = 0;
59         $this->testdb->insert_record('course', $course);
60         $syscontext = get_system_context(false);
62     /// Install the roles system.
63         $coursecreatorrole  = create_role(get_string('coursecreators'), 'coursecreator',
64                                           get_string('coursecreatorsdescription'), 'coursecreator');
65         $editteacherrole    = create_role(get_string('defaultcourseteacher'), 'editingteacher',
66                                           get_string('defaultcourseteacherdescription'), 'editingteacher');
67         $noneditteacherrole = create_role(get_string('noneditingteacher'), 'teacher',
68                                           get_string('noneditingteacherdescription'), 'teacher');
69         $studentrole        = create_role(get_string('defaultcoursestudent'), 'student',
70                                           get_string('defaultcoursestudentdescription'), 'student');
71         $guestrole          = create_role(get_string('guest'), 'guest',
72                                           get_string('guestdescription'), 'guest');
73         $userrole           = create_role(get_string('authenticateduser'), 'user',
74                                           get_string('authenticateduserdescription'), 'user');
76         /// Now is the correct moment to install capabilities - after creation of legacy roles, but before assigning of roles
77         update_capabilities('moodle');
78         update_capabilities('mod_forum');
79         update_capabilities('mod_quiz');
81         // Create some nested contexts. instanceid does not matter for this. Just
82         // ensure we don't violate any unique keys by using an unlikely number.
83         // We will fix paths in a second.
84         $contexts = $this->load_test_data('context',
85                 array('contextlevel', 'instanceid', 'path', 'depth'), array(
86            1 => array(40, 666, '', 2),
87            2 => array(50, 666, '', 3),
88            3 => array(70, 666, '', 4),
89         ));
90         $contexts[0] = $syscontext;
91         $contexts[1]->path = $contexts[0]->path . '/' . $contexts[1]->id;
92         $this->testdb->set_field('context', 'path', $contexts[1]->path, array('id' => $contexts[1]->id));
93         $contexts[2]->path = $contexts[1]->path . '/' . $contexts[2]->id;
94         $this->testdb->set_field('context', 'path', $contexts[2]->path, array('id' => $contexts[2]->id));
95         $contexts[3]->path = $contexts[2]->path . '/' . $contexts[3]->id;
96         $this->testdb->set_field('context', 'path', $contexts[3]->path, array('id' => $contexts[3]->id));
98         // Now make some test users.
99         $users = $this->load_test_data('user',
100                  array('username', 'confirmed', 'deleted'), array(
101         'a' =>   array('a',         1,           0),
102         'cc' =>  array('cc',        1,           0),
103         't1' =>  array('t1',        1,           0),
104         's1' =>  array('s1',        1,           0),
105         's2' =>  array('s2',        1,           0),
106         'del' => array('del',       1,           1),
107         'unc' => array('unc',       0,           0),
108         ));
110         // Get some of the standard roles.
111         $creator = $this->testdb->get_record('role', array('shortname' => 'coursecreator'));
112         $teacher = $this->testdb->get_record('role', array('shortname' => 'editingteacher'));
113         $student = $this->testdb->get_record('role', array('shortname' => 'student'));
114         $authuser = $this->testdb->get_record('role', array('shortname' => 'user'));
116         // And some role assignments.
117         $ras = $this->load_test_data('role_assignments',
118                 array('userid', 'roleid', 'contextid'), array(
119         'cc' => array($users['cc']->id, $creator->id, $contexts[1]->id),
120         't1' => array($users['t1']->id, $teacher->id, $contexts[2]->id),
121         's1' => array($users['s1']->id, $student->id, $contexts[2]->id),
122         's2' => array($users['s2']->id, $student->id, $contexts[2]->id),
123         ));
125         // And make user a into admin
126         $CFG->siteadmins = $users['a']->id;
128         // And some group memebership.
129         $gms = $this->load_test_data('groups_members',
130                 array('userid', 'groupid'), array(
131                 array($users['t1']->id, 666),
132                 array($users['s1']->id, 666),
133                 array($users['s2']->id, 667),
134         ));
136         // Test some simple cases - check that looking in coruse and module contextlevel gives the same answer.
137         foreach (array(2, 3) as $conindex) {
138             $results = get_users_by_capability($contexts[$conindex], 'mod/forum:replypost');
139             // note: admin accounts are never returned, so no admin return here
140             $this->assert(new ArraysHaveSameValuesExpectation(
141                     array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
142                     array_map(create_function('$o', 'return $o->id;'),
143                     $results));
144             // Paging.
145             $firstuser = reset($results);
146             $this->assertEqual(array($firstuser->id => $firstuser), get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', 0, 1));
147             $seconduser = next($results);
148             $this->assertEqual(array($seconduser->id => $seconduser), get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', 1, 1));
149             // $doanything = false (ignored now)
150             $this->assert(new ArraysHaveSameValuesExpectation(
151                     array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
152                     array_map(create_function('$o', 'return $o->id;'),
153                     get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', '', '', false)));
154             // group
155             $this->assert(new ArraysHaveSameValuesExpectation(
156                     array($users['t1']->id, $users['s1']->id)),
157                     array_map(create_function('$o', 'return $o->id;'),
158                     get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 666)));
159             // exceptions
160             $this->assert(new ArraysHaveSameValuesExpectation(
161                     array($users['s1']->id, $users['s2']->id)),
162                     array_map(create_function('$o', 'return $o->id;'),
163                     get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', '', array($users['t1']->id))));
164             $this->assert(new ArraysHaveSameValuesExpectation(
165                     array($users['s1']->id)),
166                     array_map(create_function('$o', 'return $o->id;'),
167                     get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 666, array($users['t1']->id))));
168             // $useviewallgroups
169             $this->assert(new ArraysHaveSameValuesExpectation(
170                     array($users['t1']->id, $users['s2']->id)),
171                     array_map(create_function('$o', 'return $o->id;'),
172                     get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 667, '', false, false, true)));
173             // More than one capability.
174             $this->assert(new ArraysHaveSameValuesExpectation(
175                     array($users['s1']->id, $users['s2']->id)),
176                     array_map(create_function('$o', 'return $o->id;'),
177                     get_users_by_capability($contexts[$conindex], array('mod/quiz:attempt', 'mod/quiz:reviewmyattempts'))));
178         }
180 // For reference: get_users_by_capability argument order:
181 // $context, $capability, $fields='', $sort='', $limitfrom='', $limitnum='',
182 // $groups='', $exceptions='', $doanything=true, $view=false, $useviewallgroups=false
184         // Now add some role overrides.
185         $rcs = $this->load_test_data('role_capabilities',
186                 array('capability',                 'roleid',      'contextid',      'permission'), array(
187                 array('mod/forum:replypost',        $student->id,  $contexts[1]->id, CAP_PREVENT),
188                 array('mod/forum:replypost',        $student->id,  $contexts[3]->id, CAP_ALLOW),
189                 array('mod/quiz:attempt',           $student->id,  $contexts[2]->id, CAP_PREVENT),
190                 array('mod/forum:startdiscussion',  $student->id,  $contexts[1]->id, CAP_PROHIBIT),
191                 array('mod/forum:startdiscussion',  $student->id,  $contexts[3]->id, CAP_ALLOW),
192                 array('mod/forum:viewrating',       $authuser->id, $contexts[1]->id, CAP_PROHIBIT),
193                 array('mod/forum:createattachment', $authuser->id, $contexts[3]->id, CAP_PREVENT),
194         ));
196         // Now test the overridden cases.
197         // Students prevented at category level, with and without doanything.
198         $this->assert(new ArraysHaveSameValuesExpectation(
199                 array($users['t1']->id)),
200                 array_map(create_function('$o', 'return $o->id;'),
201                 get_users_by_capability($contexts[2], 'mod/forum:replypost')));
202         $this->assert(new ArraysHaveSameValuesExpectation(
203                 array($users['t1']->id)),
204                 array_map(create_function('$o', 'return $o->id;'),
205                 get_users_by_capability($contexts[2], 'mod/forum:replypost', '', '', '', '', '', '', false)));
206         // Students prevented at category level, but re-allowed at module level, with and without doanything.
207         $this->assert(new ArraysHaveSameValuesExpectation(
208                 array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
209                 array_map(create_function('$o', 'return $o->id;'),
210                 get_users_by_capability($contexts[3], 'mod/forum:replypost', '', '', '', '', '', '', false)));
211         $this->assert(new ArraysHaveSameValuesExpectation(
212                 array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
213                 array_map(create_function('$o', 'return $o->id;'),
214                 get_users_by_capability($contexts[3], 'mod/forum:replypost')));
215         // Students prohibited at category level, re-allowed at module level should have no effect.
216         $this->assert(new ArraysHaveSameValuesExpectation(
217                 array($users['t1']->id)),
218                 array_map(create_function('$o', 'return $o->id;'),
219                 get_users_by_capability($contexts[2], 'mod/forum:startdiscussion')));
220         $this->assert(new ArraysHaveSameValuesExpectation(
221                 array($users['t1']->id)),
222                 array_map(create_function('$o', 'return $o->id;'),
223                 get_users_by_capability($contexts[3], 'mod/forum:startdiscussion')));
224         // Prevent on logged-in user should be overridden by student allow.
225         $this->assert(new ArraysHaveSameValuesExpectation(
226                 array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
227                 array_map(create_function('$o', 'return $o->id;'),
228                 get_users_by_capability($contexts[3], 'mod/forum:createattachment')));
230         // Prohibit on logged-in user should trump student/teacher allow.
231         $this->assert(new ArraysHaveSameValuesExpectation(
232                 array()),
233                 array_map(create_function('$o', 'return $o->id;'),
234                 get_users_by_capability($contexts[3], 'mod/forum:viewrating')));
236         // More than one capability, where students have one, but not the other.
237         $this->assert(new ArraysHaveSameValuesExpectation(
238                 array($users['s1']->id, $users['s2']->id)),
239                 array_map(create_function('$o', 'return $o->id;'),
240                 get_users_by_capability($contexts[3], array('mod/quiz:attempt', 'mod/quiz:reviewmyattempts'), '', '', '', '', '', '', false)));
241     }
243     function test_get_switchable_roles() {
244         global $USER;
246         $tablenames = array('role' , 'role_capabilities', 'role_assignments', 'role_allow_switch',
247                 'capabilities', 'context', 'role_names');
248         $this->create_test_tables($tablenames, 'lib');
250         $this->switch_to_test_db();
251         $this->switch_to_test_cfg();
253         // Ensure SYSCONTEXTID is set.
254         get_context_instance(CONTEXT_SYSTEM);
256         $contexts = $this->load_test_data('context',
257                  array('contextlevel', 'instanceid', 'path', 'depth'), array(
258         'sys' => array(CONTEXT_SYSTEM,     0, '/' . SYSCONTEXTID, 1),
259         'cat' => array(CONTEXT_COURSECAT, 66, '/' . SYSCONTEXTID . '/' . (SYSCONTEXTID + 1), 2),
260         'cou' => array(CONTEXT_COURSE,   666, '/' . SYSCONTEXTID . '/' . (SYSCONTEXTID + 1) . '/' . (SYSCONTEXTID + 2), 3),
261         'fp'  => array(CONTEXT_COURSE,   SITEID, '/' . SYSCONTEXTID . '/' . SITEID, 2)));
262         $this->testdb->set_field('context', 'id', SYSCONTEXTID, array('id' => $contexts['sys']->id));
263         $this->testdb->set_field('context', 'id', SYSCONTEXTID + 1, array('id' => $contexts['cat']->id));
264         $this->testdb->set_field('context', 'id', SYSCONTEXTID + 2, array('id' => $contexts['cou']->id));
265         $syscontext = $contexts['sys'];
266         $syscontext->id = SYSCONTEXTID;
267         $context = $contexts['cou'];
268         $context->id = SYSCONTEXTID + 2;
270         $roles = $this->load_test_data('role',
271                    array( 'name', 'shortname', 'description', 'sortorder'), array(
272         'r1' =>    array(   'r1',        'r1',    'not null',          2),
273         'r2' =>    array(   'r2',        'r2',    'not null',          3),
274         'funny' => array('funny',     'funny',    'not null',          4)));
275         $r1id = $roles['r1']->id;
276         $r2id = $roles['r2']->id;
277         $funnyid = $roles['funny']->id; // strange role
279         // Note that get_switchable_roles requires at least one capability for
280         // each role. I am not really clear why it was implemented that way
281         // but this makes the test work.
282         $roles = $this->load_test_data('role_capabilities',
283                 array('roleid', 'capability'), array(
284                     array($r1id, 'moodle/say:hello'),
285                     array($r2id, 'moodle/say:hello'),
286                     array($funnyid, 'moodle/say:hello'),
287         ));
289         $this->load_test_data('role_assignments',
290                 array('userid', 'contextid',   'roleid'), array(
291                 array(      2, SYSCONTEXTID + 1 , $r1id),
292                 array(      3, SYSCONTEXTID + 2 , $r2id)));
294         $this->load_test_data('role_allow_switch',
295                 array('roleid', 'allowswitch'), array(
296                 array(  $r1id ,        $r2id),
297                 array(  $r2id ,        $r1id),
298                 array(  $r2id ,        $r2id),
299                 array(  $r2id ,     $funnyid)));
301         // r1 should be able to switch to r2, but this user only has r1 in $context, not $syscontext.
302         $this->switch_global_user_id(2);
303         accesslib_clear_all_caches_for_unit_testing();
304         $this->assert(new ArraysHaveSameValuesExpectation(array()), array_keys(get_switchable_roles($syscontext)));
305         $this->assert(new ArraysHaveSameValuesExpectation(array($r2id)), array_keys(get_switchable_roles($context)));
306         $this->revert_global_user_id();
308         // The table says r2 should be able to switch to all of r1, r2 and funny;
309         // this used to be restricted further beyond the switch table (for example
310         // to prevent you switching to roles with doanything) but is not any more
311         // (for example because doanything does not exist).
312         $this->switch_global_user_id(3);
313         accesslib_clear_all_caches_for_unit_testing();
314         $this->assert(new ArraysHaveSameValuesExpectation(array()), array_keys(get_switchable_roles($syscontext)));
315         $this->assert(new ArraysHaveSameValuesExpectation(array($r2id, $r1id, $funnyid)), array_keys(get_switchable_roles($context)));
316     }