MDL-29602 accesslib improvements

Refactoring and improvements of the accesslib.php library including prevention of access for not-logged-in users when forcelogin enabled, improved context caching, OOP refactoring of contexts, fixed context loading, deduplication of role definitions in user sessions, installation improvements, decoupling of enrolment checking from capability loading, added detection of deleted and non-existent users in has_capability(), new function accesslib test, auth and enrol upgrade notes.

More details are available in tracker subtasks.
This commit is contained in:
Petr Skoda
2011-10-16 14:05:18 +02:00
parent 6731a04d93
commit e922fe23b6
26 changed files with 4859 additions and 3124 deletions
+8 -4
View File
@@ -65,13 +65,15 @@ if ($requestedqtype) {
// Get the question counts, and all the context information, for each
// context. That is, rows of these results can be used as $context objects.
$ctxpreload = context_helper::get_preload_record_columns_sql('con');
$ctxgroupby = implode(',', array_keys(context_helper::get_preload_record_columns('con')));
$counts = $DB->get_records_sql("
SELECT qc.contextid, count(1) as numquestions, sum(hidden) as numhidden, con.id, con.contextlevel, con.instanceid, con.path, con.depth
SELECT qc.contextid, count(1) as numquestions, sum(hidden) as numhidden, $ctxpreload
FROM {question} q
JOIN {question_categories} qc ON q.category = qc.id
JOIN {context} con ON con.id = qc.contextid
$sqlqtypetest
GROUP BY contextid, con.id, con.contextlevel, con.instanceid, con.path, con.depth
GROUP BY qc.contextid, $ctxgroupby
ORDER BY numquestions DESC, numhidden ASC, con.contextlevel ASC, con.id ASC", $params);
// Print the report heading.
@@ -94,8 +96,10 @@ if ($requestedqtype) {
$totalhidden = 0;
foreach ($counts as $count) {
// Work out a link for editing questions in this context.
$contextname = print_context_name($count);
$url = question_edit_url($count);
context_helper::preload_from_record($count);
$context = context::instance_by_id($count->contextid);
$contextname = $context->get_context_name();
$url = question_edit_url($context);
if ($url) {
$contextname = '<a href="' . $url . '" title="' .
get_string('editquestionshere', 'report_questioninstances') .
+5 -4
View File
@@ -96,7 +96,8 @@ foreach ($contexts as $conid => $con) {
/// Put the contexts into a tree structure.
foreach ($contexts as $conid => $con) {
$parentcontextid = get_parent_contextid($con);
$context = context::instance_by_id($conid);
$parentcontextid = get_parent_contextid($context);
if ($parentcontextid) {
$contexts[$parentcontextid]->children[] = $conid;
}
@@ -156,13 +157,13 @@ function print_report_tree($contextid, $contexts, $systemcontext, $fullname) {
}
// Pull the current context into an array for convinience.
$context = $contexts[$contextid];
$context = context::instance_by_id($contextid);
// Print the context name.
echo $OUTPUT->heading(print_context_name($contexts[$contextid]), 4, 'contextname');
echo $OUTPUT->heading($context->get_context_name(), 4, 'contextname');
// If there are any role assignments here, print them.
foreach ($context->roleassignments as $ra) {
foreach ($contexts[$contextid]->roleassignments as $ra) {
$value = $ra->contextid . ',' . $ra->roleid;
$inputid = 'unassign' . $value;
+4 -2
View File
@@ -138,7 +138,8 @@ if ($capability) {
// Put the contexts into a tree structure.
foreach ($contexts as $conid => $con) {
$parentcontextid = get_parent_contextid($con);
$context = context::instance_by_id($conid);
$parentcontextid = get_parent_contextid($context);
if ($parentcontextid) {
$contexts[$parentcontextid]->children[] = $conid;
}
@@ -196,7 +197,8 @@ function print_report_tree($contextid, $contexts, $allroles) {
$url = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$contextid";
$title = get_string('changeoverrides', 'tool_capability');
}
echo '<h3><a href="' . $url . '" title="' . $title . '">', print_context_name($contexts[$contextid]), '</a></h3>';
$context = context::instance_by_id($contextid);
echo '<h3><a href="' . $url . '" title="' . $title . '">', $context->get_context_name(), '</a></h3>';
// If there are any role overrides here, print them.
if (!empty($contexts[$contextid]->rolecapabilities)) {
+3 -4
View File
@@ -39,7 +39,9 @@
if ($shibbolethauth->user_login($frm->username, $frm->password)) {
$USER = authenticate_user_login($frm->username, $frm->password);
$user = authenticate_user_login($frm->username, $frm->password);
enrol_check_plugins($user);
session_set_user($user);
$USER->loggedin = true;
$USER->site = $CFG->wwwroot; // for added security, store the site in the
@@ -75,9 +77,6 @@
}
}
enrol_check_plugins($USER);
load_all_capabilities(); /// This is what lets the user do anything on the site :-)
redirect($urltogo);
exit;
+10
View File
@@ -0,0 +1,10 @@
This files describes API changes in /auth/* - plugins,
information provided here is intended especially for developers.
=== 2.2 ===
required changes in code:
* the correct sequence to set up global $USER is:
$user = get_complete_user_data('username', $username); // or $user = authenticate_user_login()
enrol_check_plugins($user);
session_set_user($user);
+2 -2
View File
@@ -143,9 +143,9 @@ if (!empty($entry->id)) {
if ($CFG->useblogassociations && ($blogassociations = $DB->get_records('blog_association', array('blogid' => $entry->id)))) {
foreach ($blogassociations as $assocrec) {
$contextrec = $DB->get_record('context', array('id' => $assocrec->contextid));
$context = get_context_instance_by_id($assocrec->contextid);
switch ($contextrec->contextlevel) {
switch ($context->contextlevel) {
case CONTEXT_COURSE:
$entry->courseassoc = $assocrec->contextid;
break;
+4 -4
View File
@@ -95,7 +95,7 @@ class blog_edit_form extends moodleform {
$a->modname = $mod->name;
$context = get_context_instance(CONTEXT_MODULE, $modid);
} else {
$context = $DB->get_record('context', array('id' => $entry->modassoc));
$context = get_context_instance_by_id($entry->modassoc);
$cm = $DB->get_record('course_modules', array('id' => $context->instanceid));
$a = new stdClass();
$a->modtype = $DB->get_field('modules', 'name', array('id' => $cm->module));
@@ -134,7 +134,7 @@ class blog_edit_form extends moodleform {
// validate course association
if (!empty($data['courseassoc']) && has_capability('moodle/blog:associatecourse', $sitecontext)) {
$coursecontext = $DB->get_record('context', array('id' => $data['courseassoc'], 'contextlevel' => CONTEXT_COURSE));
$coursecontext = get_context_instance(CONTEXT_COURSE, $data['courseassoc']);
if ($coursecontext) {
if (!is_enrolled($coursecontext) and !is_viewing($coursecontext)) {
@@ -149,12 +149,12 @@ class blog_edit_form extends moodleform {
if (!empty($data['modassoc'])) {
$modcontextid = $data['modassoc'];
$modcontext = $DB->get_record('context', array('id' => $modcontextid, 'contextlevel' => CONTEXT_MODULE));
$modcontext = get_context_instance(CONTEXT_MODULE, $modcontextid);
if ($modcontext) {
// get context of the mod's course
$path = explode('/', $modcontext->path);
$coursecontext = $DB->get_record('context', array('id' => $path[(count($path) - 2)]));
$coursecontext = get_context_instance_by_id($path[(count($path) - 2)]);
// ensure only one course is associated
if (!empty($data['courseassoc'])) {
+7 -7
View File
@@ -247,10 +247,10 @@ class blog_entry {
// First find and show the associated course
foreach ($blogassociations as $assocrec) {
$contextrec = $DB->get_record('context', array('id' => $assocrec->contextid));
if ($contextrec->contextlevel == CONTEXT_COURSE) {
$assocurl = new moodle_url('/course/view.php', array('id' => $contextrec->instanceid));
$text = $DB->get_field('course', 'shortname', array('id' => $contextrec->instanceid)); //TODO: performance!!!!
$context = get_context_instance_by_id($assocrec->contextid);
if ($context->contextlevel == CONTEXT_COURSE) {
$assocurl = new moodle_url('/course/view.php', array('id' => $context->instanceid));
$text = $DB->get_field('course', 'shortname', array('id' => $context->instanceid)); //TODO: performance!!!!
$assocstr .= $OUTPUT->action_icon($assocurl, new pix_icon('i/course', $text), null, array(), true);
$hascourseassocs = true;
$assoctype = get_string('course');
@@ -259,15 +259,15 @@ class blog_entry {
// Now show mod association
foreach ($blogassociations as $assocrec) {
$contextrec = $DB->get_record('context', array('id' => $assocrec->contextid));
$context = get_context_instance_by_id($assocrec->contextid);
if ($contextrec->contextlevel == CONTEXT_MODULE) {
if ($context->contextlevel == CONTEXT_MODULE) {
if ($hascourseassocs) {
$assocstr .= ', ';
$hascourseassocs = false;
}
$modinfo = $DB->get_record('course_modules', array('id' => $contextrec->instanceid));
$modinfo = $DB->get_record('course_modules', array('id' => $context->instanceid));
$modname = $DB->get_field('modules', 'name', array('id' => $modinfo->module));
$assocurl = new moodle_url('/mod/'.$modname.'/view.php', array('id' => $modinfo->id));
+2 -2
View File
@@ -77,7 +77,7 @@ class enrol_guest_plugin extends enrol_plugin {
if (empty($instance->password)) {
// Temporarily assign them some guest role for this context
$context = get_context_instance(CONTEXT_COURSE, $instance->courseid);
$USER->access = load_temp_role($context, $CFG->guestroleid, $USER->access);
load_temp_course_role($context, $CFG->guestroleid);
return ENROL_REQUIRE_LOGIN_CACHE_PERIOD + time();
}
@@ -131,7 +131,7 @@ class enrol_guest_plugin extends enrol_plugin {
// add guest role
$context = get_context_instance(CONTEXT_COURSE, $instance->courseid);
$USER->access = load_temp_role($context, $CFG->guestroleid, $USER->access);
load_temp_course_role($context, $CFG->guestroleid);
// go to the originally requested page
if (!empty($SESSION->wantsurl)) {
-3
View File
@@ -38,9 +38,6 @@ $context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
require_login();
// Refreshing enrolment data in the USER session
load_all_capabilities();
if ($SESSION->wantsurl) {
$destination = $SESSION->wantsurl;
unset($SESSION->wantsurl);
+8
View File
@@ -0,0 +1,8 @@
This files describes API changes in /enrol/* - plugins,
information provided here is intended especially for developers.
=== 2.2 ===
required changes in code:
* load_temp_role() is deprecated, use load_temp_course_role() instead, temp role not loaded
* remove_temp_role() is deprecated, use remove_temp_course_roles() instead
+3533 -2645
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -1014,7 +1014,11 @@ function fix_course_sortorder() {
// now fix the paths and depths in context table if needed
if ($fixcontexts) {
rebuild_contexts($fixcontexts);
foreach ($fixcontexts as $fixcontext) {
$fixcontext->reset_paths(false);
}
context_helper::build_all_paths(false);
unset($fixcontexts);
}
// release memory
+49 -20
View File
@@ -29,14 +29,17 @@ defined('MOODLE_INTERNAL') || die();
function xmldb_main_install() {
global $CFG, $DB, $SITE;
/// make sure system context exists
$syscontext = get_system_context(false);
if ($syscontext->id != 1) {
/// Make sure system context exists
$syscontext = context_system::instance(0, MUST_EXIST, false);
if ($syscontext->id != SYSCONTEXTID) {
throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new system context id!');
}
/// create site course
/// Create site course
if ($DB->record_exists('course', array())) {
throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create frontpage course, courses already exist.');
}
$newsite = new stdClass();
$newsite->fullname = '';
$newsite->shortname = '';
@@ -48,18 +51,38 @@ function xmldb_main_install() {
$newsite->timecreated = time();
$newsite->timemodified = $newsite->timecreated;
$newsite->id = $DB->insert_record('course', $newsite);
if (defined('SITEID')) {
$newsite->id = SITEID;
$DB->import_record('course', $newsite);
$DB->get_manager()->reset_sequence('course');
} else {
$newsite->id = $DB->insert_record('course', $newsite);
define('SITEID', $newsite->id);
}
$SITE = get_site();
if ($newsite->id != 1 or $SITE->id != 1) {
if ($newsite->id != $SITE->id) {
throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new site course id!');
}
// Make sure site course context exists
context_course::instance($SITE->id);
// Update the global frontpage cache
$SITE = $DB->get_record('course', array('id'=>$newsite->id), '*', MUST_EXIST);
/// make sure site course context exists
get_context_instance(CONTEXT_COURSE, $SITE->id);
/// Create default course category
if ($DB->record_exists('course_categories', array())) {
throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default course category, categories already exist.');
}
$cat = new stdClass();
$cat->name = get_string('miscellaneous');
$cat->depth = 1;
$cat->sortorder = MAX_COURSES_IN_CATEGORY;
$cat->timemodified = time();
$catid = $DB->insert_record('course_categories', $cat);
$DB->set_field('course_categories', 'path', '/'.$catid, array('id'=>$catid));
// Make sure category context exists
context_coursecat::instance($catid);
/// create default course category
$cat = get_course_category();
$defaults = array(
'rolesactive' => '0', // marks fully set up system
@@ -82,7 +105,7 @@ function xmldb_main_install() {
}
/// bootstrap mnet
/// Bootstrap mnet
$mnethost = new stdClass();
$mnethost->wwwroot = $CFG->wwwroot;
$mnethost->name = '';
@@ -137,7 +160,10 @@ function xmldb_main_install() {
$mnetallhosts->id = $DB->insert_record('mnet_host', $mnetallhosts, true);
set_config('mnet_all_hosts_id', $mnetallhosts->id);
/// Create guest record - do not assign any role, guest user get's the default guest role automatically on the fly
/// Create guest record - do not assign any role, guest user gets the default guest role automatically on the fly
if ($DB->record_exists('user', array())) {
throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default users, users already exist.');
}
$guest = new stdClass();
$guest->auth = 'manual';
$guest->username = 'guest';
@@ -156,6 +182,8 @@ function xmldb_main_install() {
}
// Store guest id
set_config('siteguest', $guest->id);
// Make sure user context exists
context_user::instance($guest->id);
/// Now create admin user
@@ -176,8 +204,10 @@ function xmldb_main_install() {
if ($admin->id != 2) {
throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new admin user id!');
}
/// Store list of admins
// Store list of admins
set_config('siteadmins', $admin->id);
// Make sure user context exists
context_user::instance($admin->id);
/// Install the roles system.
@@ -264,17 +294,16 @@ function xmldb_main_install() {
require_once($CFG->libdir . '/licenselib.php');
license_manager::install_licenses();
/// Add two lines of data into this new table
// Init profile pages defaults
if ($DB->record_exists('my_pages', array())) {
throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default profile pages, records already exist.');
}
$mypage = new stdClass();
$mypage->userid = NULL;
$mypage->name = '__default';
$mypage->private = 0;
$mypage->sortorder = 0;
if (!$DB->record_exists('my_pages', array('userid'=>NULL, 'private'=>0))) {
$DB->insert_record('my_pages', $mypage);
}
$DB->insert_record('my_pages', $mypage);
$mypage->private = 1;
if (!$DB->record_exists('my_pages', array('userid'=>NULL, 'private'=>1))) {
$DB->insert_record('my_pages', $mypage);
}
$DB->insert_record('my_pages', $mypage);
}
+7 -20
View File
@@ -280,28 +280,28 @@ function isteacher() {
* @deprecated
*/
function isteacherinanycourse() {
error('Function isteacherinanycourse() was removed, please use capabilities instead!');
throw new coding_Exception('Function isteacherinanycourse() was removed, please use capabilities instead!');
}
/**
* @deprecated
*/
function get_guest() {
error('Function get_guest() was removed, please use capabilities instead!');
throw new coding_Exception('Function get_guest() was removed, please use capabilities instead!');
}
/**
* @deprecated
*/
function isguest() {
error('Function isguest() was removed, please use capabilities instead!');
throw new coding_Exception('Function isguest() was removed, please use capabilities instead!');
}
/**
* @deprecated
*/
function get_teacher() {
error('Function get_teacher() was removed, please use capabilities instead!');
throw new coding_Exception('Function get_teacher() was removed, please use capabilities instead!');
}
/**
@@ -371,15 +371,7 @@ function get_recent_enrolments($courseid, $timestart) {
* @return object
*/
function make_context_subobj($rec) {
$ctx = new StdClass;
$ctx->id = $rec->ctxid; unset($rec->ctxid);
$ctx->path = $rec->ctxpath; unset($rec->ctxpath);
$ctx->depth = $rec->ctxdepth; unset($rec->ctxdepth);
$ctx->contextlevel = $rec->ctxlevel; unset($rec->ctxlevel);
$ctx->instanceid = $rec->id;
$rec->context = $ctx;
return $rec;
throw new coding_Exception('make_context_subobj() was removed, use new context preloading');
}
/**
@@ -395,10 +387,7 @@ function make_context_subobj($rec) {
* for this thing.
*/
function is_context_subobj_valid($rec, $contextlevel) {
return isset($rec->context) && isset($rec->context->id) &&
isset($rec->context->path) && isset($rec->context->depth) &&
isset($rec->context->contextlevel) && isset($rec->context->instanceid) &&
$rec->context->contextlevel == $contextlevel && $rec->context->instanceid == $rec->id;
throw new coding_Exception('is_context_subobj_valid() was removed, use new context preloading');
}
/**
@@ -415,9 +404,7 @@ function is_context_subobj_valid($rec, $contextlevel) {
* @param integer $contextlevel the type of thing $rec is, one of the CONTEXT_... constants.
*/
function ensure_context_subobj_present(&$rec, $contextlevel) {
if (!is_context_subobj_valid($rec, $contextlevel)) {
$rec->context = get_context_instance($contextlevel, $rec->id);
}
throw new coding_Exception('ensure_context_subobj_present() was removed, use new context preloading');
}
########### FROM weblib.php ##########################################################################
+6 -4
View File
@@ -188,7 +188,9 @@ function enrol_is_enabled($enrol) {
* Check all the login enrolment information for the given user object
* by querying the enrolment plugins
*
* @param object $user
* This function may be very slow, use only once after log-in or login-as.
*
* @param stdClass $user
* @return void
*/
function enrol_check_plugins($user) {
@@ -201,7 +203,7 @@ function enrol_check_plugins($user) {
if (is_siteadmin()) {
// no sync for admin user, please use admin accounts only for admin tasks like the unix root user!
// if plugin fails on sync admins need to be able to log in
// if plugin fails on sync admins need to be able to log in and fix the settings
return;
}
@@ -1150,7 +1152,7 @@ abstract class enrol_plugin {
}
if (isset($USER->enrol['tempguest'][$courseid])) {
unset($USER->enrol['tempguest'][$courseid]);
$USER->access = remove_temp_roles($context, $USER->access);
remove_temp_course_roles($context);
}
}
}
@@ -1272,7 +1274,7 @@ abstract class enrol_plugin {
}
if (isset($USER->enrol['tempguest'][$courseid])) {
unset($USER->enrol['tempguest'][$courseid]);
$USER->access = remove_temp_roles($context, $USER->access);
remove_temp_course_roles($context);
}
}
}
+9 -5
View File
@@ -2790,7 +2790,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $
} else {
//expired
unset($USER->enrol['tempguest'][$course->id]);
$USER->access = remove_temp_roles($coursecontext, $USER->access);
remove_temp_course_roles($coursecontext);
}
}
@@ -2817,7 +2817,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $
$access = true;
// remove traces of previous temp guest access
$USER->access = remove_temp_roles($coursecontext, $USER->access);
remove_temp_course_roles($coursecontext);
} else {
$instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC');
@@ -2831,7 +2831,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $
$until = $enrols[$instance->enrol]->try_autoenrol($instance);
if ($until !== false) {
$USER->enrol['enrolled'][$course->id] = $until;
$USER->access = remove_temp_roles($coursecontext, $USER->access);
remove_temp_course_roles($coursecontext);
$access = true;
break;
}
@@ -3031,6 +3031,7 @@ function require_user_key_login($script, $instance=null) {
}
/// emulate normal session
enrol_check_plugins($user);
session_set_user($user);
/// note we are not using normal login
@@ -3882,12 +3883,15 @@ function complete_user_login($user) {
// this helps prevent session fixation attacks from the same domain
session_regenerate_id(true);
// let enrol plugins deal with new enrolments if necessary
enrol_check_plugins($user);
// check enrolments, load caps and setup $USER object
session_set_user($user);
// reload preferences from DB
unset($user->preference);
check_user_preferences_loaded($user);
unset($USER->preference);
check_user_preferences_loaded($USER);
// update login times
update_user_login_times();
+1 -1
View File
@@ -2868,7 +2868,7 @@ class settings_navigation extends navigation_node {
$context = $this->context;
if ($context->contextlevel == CONTEXT_BLOCK) {
$this->load_block_settings();
$context = $DB->get_record_sql('SELECT ctx.* FROM {block_instances} bi LEFT JOIN {context} ctx ON ctx.id=bi.parentcontextid WHERE bi.id=?', array($context->instanceid));
$context = $context->get_parent_context();
}
switch ($context->contextlevel) {
+6 -6
View File
@@ -894,7 +894,8 @@ function get_moodle_cookie() {
/**
* Setup $USER object - called during login, loginas, etc.
* Preloads capabilities and checks enrolment plugins
*
* Call sync_user_enrolments() manually after log-in, or log-in-as.
*
* @param stdClass $user full user record object
* @return void
@@ -902,11 +903,6 @@ function get_moodle_cookie() {
function session_set_user($user) {
$_SESSION['USER'] = $user;
unset($_SESSION['USER']->description); // conserve memory
if (!isset($_SESSION['USER']->access)) {
// check enrolments and load caps only once
enrol_check_plugins($_SESSION['USER']);
load_all_capabilities();
}
sesskey(); // init session key
}
@@ -950,6 +946,10 @@ function session_loginas($userid, $context) {
$user = get_complete_user_data('id', $userid);
$user->realuser = $_SESSION['REALUSER']->id;
$user->loginascontext = $context;
// let enrol plugins deal with new enrolments if necessary
enrol_check_plugins($user);
// set up global $USER
session_set_user($user);
}
File diff suppressed because it is too large Load Diff
-382
View File
@@ -1,382 +0,0 @@
<?php
/**
* Unit tests for (some of) ../accesslib.php.
*
* @copyright &copy; 2006 The Open University
* @author [email protected]
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package moodlecore
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
class accesslib_test extends UnitTestCaseUsingDatabase {
public static $includecoverage = array('lib/accesslib.php');
function test_get_parent_contexts() {
$context = get_context_instance(CONTEXT_SYSTEM);
$this->assertEqual(get_parent_contexts($context), array());
$context = new stdClass;
$context->path = '/1/25';
$this->assertEqual(get_parent_contexts($context), array(1));
$context = new stdClass;
$context->path = '/1/123/234/345/456';
$this->assertEqual(get_parent_contexts($context), array(345, 234, 123, 1));
}
function test_get_parent_contextid() {
$context = get_context_instance(CONTEXT_SYSTEM);
$this->assertFalse(get_parent_contextid($context));
$context = new stdClass;
$context->path = '/1/25';
$this->assertEqual(get_parent_contextid($context), 1);
$context = new stdClass;
$context->path = '/1/123/234/345/456';
$this->assertEqual(get_parent_contextid($context), 345);
}
function test_get_users_by_capability() {
global $CFG;
$tablenames = array('capabilities', 'context', 'role', 'role_capabilities',
'role_allow_assign', 'role_allow_override', 'role_assignments', 'role_context_levels',
'user', 'groups_members', 'cache_flags', 'events_handlers', 'user_lastaccess', 'course');
$this->create_test_tables($tablenames, 'lib');
accesslib_clear_all_caches_for_unit_testing();
$this->switch_to_test_db();
$this->switch_to_test_cfg();
$course = new stdClass();
$course->category = 0;
$this->testdb->insert_record('course', $course);
$syscontext = get_system_context(false);
/// Install the roles system.
$coursecreatorrole = create_role(get_string('coursecreators'), 'coursecreator',
get_string('coursecreatorsdescription'), 'coursecreator');
$editteacherrole = create_role(get_string('defaultcourseteacher'), 'editingteacher',
get_string('defaultcourseteacherdescription'), 'editingteacher');
$noneditteacherrole = create_role(get_string('noneditingteacher'), 'teacher',
get_string('noneditingteacherdescription'), 'teacher');
$studentrole = create_role(get_string('defaultcoursestudent'), 'student',
get_string('defaultcoursestudentdescription'), 'student');
$guestrole = create_role(get_string('guest'), 'guest',
get_string('guestdescription'), 'guest');
$userrole = create_role(get_string('authenticateduser'), 'user',
get_string('authenticateduserdescription'), 'user');
/// Now is the correct moment to install capabilities - after creation of legacy roles, but before assigning of roles
update_capabilities('moodle');
update_capabilities('mod_forum');
update_capabilities('mod_quiz');
// Create some nested contexts. instanceid does not matter for this. Just
// ensure we don't violate any unique keys by using an unlikely number.
// We will fix paths in a second.
$contexts = $this->load_test_data('context',
array('contextlevel', 'instanceid', 'path', 'depth'), array(
1 => array(40, 666, '', 2),
2 => array(50, 666, '', 3),
3 => array(70, 666, '', 4),
));
$contexts[0] = $syscontext;
$contexts[1]->path = $contexts[0]->path . '/' . $contexts[1]->id;
$this->testdb->set_field('context', 'path', $contexts[1]->path, array('id' => $contexts[1]->id));
$contexts[2]->path = $contexts[1]->path . '/' . $contexts[2]->id;
$this->testdb->set_field('context', 'path', $contexts[2]->path, array('id' => $contexts[2]->id));
$contexts[3]->path = $contexts[2]->path . '/' . $contexts[3]->id;
$this->testdb->set_field('context', 'path', $contexts[3]->path, array('id' => $contexts[3]->id));
// Now make some test users.
$users = $this->load_test_data('user',
array('username', 'confirmed', 'deleted'), array(
'a' => array('a', 1, 0),
'cc' => array('cc', 1, 0),
't1' => array('t1', 1, 0),
's1' => array('s1', 1, 0),
's2' => array('s2', 1, 0),
'del' => array('del', 1, 1),
'unc' => array('unc', 0, 0),
));
// Get some of the standard roles.
$creator = $this->testdb->get_record('role', array('shortname' => 'coursecreator'));
$teacher = $this->testdb->get_record('role', array('shortname' => 'editingteacher'));
$student = $this->testdb->get_record('role', array('shortname' => 'student'));
$authuser = $this->testdb->get_record('role', array('shortname' => 'user'));
// And some role assignments.
$ras = $this->load_test_data('role_assignments',
array('userid', 'roleid', 'contextid'), array(
'cc' => array($users['cc']->id, $creator->id, $contexts[1]->id),
't1' => array($users['t1']->id, $teacher->id, $contexts[2]->id),
's1' => array($users['s1']->id, $student->id, $contexts[2]->id),
's2' => array($users['s2']->id, $student->id, $contexts[2]->id),
));
// And make user a into admin
$CFG->siteadmins = $users['a']->id;
$CFG->defaultuserroleid = $userrole;
// And some group memebership.
$gms = $this->load_test_data('groups_members',
array('userid', 'groupid'), array(
array($users['t1']->id, 666),
array($users['s1']->id, 666),
array($users['s2']->id, 667),
));
// Test some simple cases - check that looking in coruse and module contextlevel gives the same answer.
foreach (array(2, 3) as $conindex) {
$results = get_users_by_capability($contexts[$conindex], 'mod/forum:replypost');
// note: admin accounts are never returned, so no admin return here
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
$results));
// Paging.
$firstuser = reset($results);
$this->assertEqual(array($firstuser->id => $firstuser), get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', 0, 1));
$seconduser = next($results);
$this->assertEqual(array($seconduser->id => $seconduser), get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', 1, 1));
// $doanything = false (ignored now)
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', '', '', false)));
// group
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id, $users['s1']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 666)));
// exceptions
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['s1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', '', array($users['t1']->id))));
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['s1']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 666, array($users['t1']->id))));
// $useviewallgroups
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 667, '', false, false, true)));
// More than one capability.
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['s1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[$conindex], array('mod/quiz:attempt', 'mod/quiz:reviewmyattempts'))));
}
// For reference: get_users_by_capability argument order:
// $context, $capability, $fields='', $sort='', $limitfrom='', $limitnum='',
// $groups='', $exceptions='', $doanything=true, $view=false, $useviewallgroups=false
// Now add some role overrides.
$rcs = $this->load_test_data('role_capabilities',
array('capability', 'roleid', 'contextid', 'permission'), array(
array('mod/forum:replypost', $student->id, $contexts[1]->id, CAP_PREVENT),
array('mod/forum:replypost', $student->id, $contexts[3]->id, CAP_ALLOW),
array('mod/quiz:attempt', $student->id, $contexts[2]->id, CAP_PREVENT),
array('mod/forum:startdiscussion', $student->id, $contexts[1]->id, CAP_PROHIBIT),
array('mod/forum:startdiscussion', $student->id, $contexts[3]->id, CAP_ALLOW),
array('mod/forum:viewrating', $authuser->id, $contexts[1]->id, CAP_PROHIBIT),
array('mod/forum:createattachment', $authuser->id, $contexts[3]->id, CAP_PREVENT),
));
// Now test the overridden cases.
// Students prevented at category level, with and without doanything.
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[2], 'mod/forum:replypost')));
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[2], 'mod/forum:replypost', '', '', '', '', '', '', false)));
// Students prevented at category level, but re-allowed at module level, with and without doanything.
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[3], 'mod/forum:replypost', '', '', '', '', '', '', false)));
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[3], 'mod/forum:replypost')));
// Students prohibited at category level, re-allowed at module level should have no effect.
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[2], 'mod/forum:startdiscussion')));
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[3], 'mod/forum:startdiscussion')));
// Prevent on logged-in user should be overridden by student allow.
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['t1']->id, $users['s1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[3], 'mod/forum:createattachment')));
// Prohibit on logged-in user should trump student/teacher allow.
$this->assert(new ArraysHaveSameValuesExpectation(
array()),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[3], 'mod/forum:viewrating')));
// More than one capability, where students have one, but not the other.
$this->assert(new ArraysHaveSameValuesExpectation(
array($users['s1']->id, $users['s2']->id)),
array_map(create_function('$o', 'return $o->id;'),
get_users_by_capability($contexts[3], array('mod/quiz:attempt', 'mod/quiz:reviewmyattempts'), '', '', '', '', '', '', false)));
}
function test_get_switchable_roles() {
global $USER;
$tablenames = array('role' , 'role_capabilities', 'role_assignments', 'role_allow_switch',
'capabilities', 'context', 'role_names');
$this->create_test_tables($tablenames, 'lib');
$this->switch_to_test_db();
$this->switch_to_test_cfg();
// Ensure SYSCONTEXTID is set.
get_context_instance(CONTEXT_SYSTEM);
$contexts = $this->load_test_data('context',
array('contextlevel', 'instanceid', 'path', 'depth'), array(
'sys' => array(CONTEXT_SYSTEM, 0, '/' . SYSCONTEXTID, 1),
'cat' => array(CONTEXT_COURSECAT, 66, '/' . SYSCONTEXTID . '/' . (SYSCONTEXTID + 1), 2),
'cou' => array(CONTEXT_COURSE, 666, '/' . SYSCONTEXTID . '/' . (SYSCONTEXTID + 1) . '/' . (SYSCONTEXTID + 2), 3),
'fp' => array(CONTEXT_COURSE, SITEID, '/' . SYSCONTEXTID . '/' . SITEID, 2)));
$this->testdb->set_field('context', 'id', SYSCONTEXTID, array('id' => $contexts['sys']->id));
$this->testdb->set_field('context', 'id', SYSCONTEXTID + 1, array('id' => $contexts['cat']->id));
$this->testdb->set_field('context', 'id', SYSCONTEXTID + 2, array('id' => $contexts['cou']->id));
$syscontext = $contexts['sys'];
$syscontext->id = SYSCONTEXTID;
$context = $contexts['cou'];
$context->id = SYSCONTEXTID + 2;
$roles = $this->load_test_data('role',
array( 'name', 'shortname', 'description', 'sortorder'), array(
'r1' => array( 'r1', 'r1', 'not null', 2),
'r2' => array( 'r2', 'r2', 'not null', 3),
'funny' => array('funny', 'funny', 'not null', 4)));
$r1id = $roles['r1']->id;
$r2id = $roles['r2']->id;
$funnyid = $roles['funny']->id; // strange role
// Note that get_switchable_roles requires at least one capability for
// each role. I am not really clear why it was implemented that way
// but this makes the test work.
$roles = $this->load_test_data('role_capabilities',
array('roleid', 'capability'), array(
array($r1id, 'moodle/say:hello'),
array($r2id, 'moodle/say:hello'),
array($funnyid, 'moodle/say:hello'),
));
$this->load_test_data('role_assignments',
array('userid', 'contextid', 'roleid'), array(
array( 2, SYSCONTEXTID + 1 , $r1id),
array( 3, SYSCONTEXTID + 2 , $r2id)));
$this->load_test_data('role_allow_switch',
array('roleid', 'allowswitch'), array(
array( $r1id , $r2id),
array( $r2id , $r1id),
array( $r2id , $r2id),
array( $r2id , $funnyid)));
// r1 should be able to switch to r2, but this user only has r1 in $context, not $syscontext.
$this->switch_global_user_id(2);
accesslib_clear_all_caches_for_unit_testing();
$this->assert(new ArraysHaveSameValuesExpectation(array()), array_keys(get_switchable_roles($syscontext)));
$this->assert(new ArraysHaveSameValuesExpectation(array($r2id)), array_keys(get_switchable_roles($context)));
$this->revert_global_user_id();
// The table says r2 should be able to switch to all of r1, r2 and funny;
// this used to be restricted further beyond the switch table (for example
// to prevent you switching to roles with doanything) but is not any more
// (for example because doanything does not exist).
$this->switch_global_user_id(3);
accesslib_clear_all_caches_for_unit_testing();
$this->assert(new ArraysHaveSameValuesExpectation(array()), array_keys(get_switchable_roles($syscontext)));
$this->assert(new ArraysHaveSameValuesExpectation(array($r2id, $r1id, $funnyid)), array_keys(get_switchable_roles($context)));
}
function test_context_cache() {
// Create cache, empty
$cache = new context_cache();
$this->assertEqual(0, $cache->get_approx_count());
// Put context in cache
$context = (object)array('id'=>37,'contextlevel'=>50,'instanceid'=>13);
$cache->add($context);
$this->assertEqual(1, $cache->get_approx_count());
// Get context out of cache
$this->assertEqual($context, $cache->get(50, 13));
$this->assertEqual($context, $cache->get_by_id(37));
// Try to get context that isn't there
$this->assertEqual(false, $cache->get(50, 99));
$this->assertEqual(false, $cache->get(99, 13));
$this->assertEqual(false, $cache->get_by_id(99));
// Remove context from cache
$cache->remove($context);
$this->assertEqual(0, $cache->get_approx_count());
$this->assertEqual(false, $cache->get(50, 13));
$this->assertEqual(false, $cache->get_by_id(37));
// Add a stack of contexts to cache
for($i=0; $i<context_cache::MAX_SIZE; $i++) {
$context = (object)array('id'=>$i, 'contextlevel'=>50,
'instanceid'=>$i+1);
$cache->add($context);
}
$this->assertEqual(context_cache::MAX_SIZE, $cache->get_approx_count());
// Get a random sample from the middle
$sample = (object)array(
'id'=>174, 'contextlevel'=>50, 'instanceid'=>175);
$this->assertEqual($sample, $cache->get(50, 175));
$this->assertEqual($sample, $cache->get_by_id(174));
// Now add one more; should result in size being reduced
$context = (object)array('id'=>99999, 'contextlevel'=>50,
'instanceid'=>99999);
$cache->add($context);
$this->assertEqual(context_cache::MAX_SIZE - context_cache::REDUCE_SIZE + 1,
$cache->get_approx_count());
// Check that the first ones are no longer there
$this->assertEqual(false, $cache->get(50, 175));
$this->assertEqual(false, $cache->get_by_id(174));
// Check that the last ones are still there
$bigid = context_cache::MAX_SIZE - 10;
$sample = (object)array(
'id'=>$bigid, 'contextlevel'=>50, 'instanceid'=>$bigid+1);
$this->assertEqual($sample, $cache->get(50, $bigid+1));
$this->assertEqual($sample, $cache->get_by_id($bigid));
// Reset the cache
$cache->reset();
$this->assertEqual(0, $cache->get_approx_count());
$this->assertEqual(false, $cache->get(50, $bigid+1));
}
}
+5 -5
View File
@@ -1417,11 +1417,11 @@ function upgrade_core($version, $verbose) {
purge_all_caches();
// Clean up contexts - more and more stuff depends on existence of paths and contexts
cleanup_contexts();
create_contexts();
build_context_path();
$syscontext = get_context_instance(CONTEXT_SYSTEM);
mark_context_dirty($syscontext->path);
context_helper::cleanup_instances();
context_helper::create_instances(null, false);
context_helper::build_all_paths(false);
$syscontext = context_system::instance();
$syscontext->mark_dirty();
print_upgrade_part_end('moodle', false, $verbose);
} catch (Exception $ex) {
+3
View File
@@ -56,6 +56,9 @@ if (!empty($user)) {
}
}
// let enrol plugins deal with new enrolments if necessary
enrol_check_plugins($user);
// setup user session to check capability
session_set_user($user);
+7 -3
View File
@@ -16,9 +16,9 @@
/**
* rss/file.php - entry point to serve rss streams
*
*
* This script simply checks the parameters to construct a $USER
* then finds and calls a function in the relevant component to
* then finds and calls a function in the relevant component to
* actually check security and create the RSS stream
*
* @package moodlecore
@@ -81,7 +81,7 @@ if ($token==="$inttoken") {
if (!isset($modinfo->instances[$componentname])) {
$modinfo->instances[$componentname] = array();
}
foreach ($modinfo->instances[$componentname] as $modinstanceid=>$cm) {
if ($modinstanceid==$instanceid) {
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
@@ -122,6 +122,10 @@ if ($token==="$inttoken") {
}
$user = get_complete_user_data('id', $userid);
// let enrol plugins deal with new enrolments if necessary
enrol_check_plugins($user);
session_set_user($user); //for login and capability checks
// Check the context actually exists
+1
View File
@@ -654,6 +654,7 @@ abstract class webservice_server implements webservice_server_interface {
}
// now fake user login, the session is completely empty too
enrol_check_plugins($user);
session_set_user($user);
$this->userid = $user->id;
+2
View File
@@ -66,6 +66,8 @@ $user = $DB->get_record('user', array('id'=>$token->userid, 'deleted'=>0), '*',
// log token access
$DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));
// let enrol plugins deal with new enrolments if necessary
enrol_check_plugins($user);
session_set_user($user);
$context = get_context_instance(CONTEXT_USER, $USER->id);
require_capability('moodle/user:manageownfiles', $context);