From 3a0c6cca332fbeced9276422e50efa9abea58d72 Mon Sep 17 00:00:00 2001 From: skodak Date: Wed, 23 Jul 2008 16:10:06 +0000 Subject: [PATCH] MDL-8521 new feature - safe overrides, see tracker for details; merged from MOODLE_19_STABLE --- admin/roles/allowoverride.php | 2 +- admin/roles/assign.php | 3 +- admin/roles/override.html | 12 ++- admin/roles/override.php | 37 +++++++- blocks/admin/block_admin.php | 12 ++- lang/en_utf8/admin.php | 1 + lang/en_utf8/role.php | 2 + lib/accesslib.php | 148 +++++++++++++++---------------- lib/db/access.php | 43 +++++++-- mod/assignment/db/access.php | 1 + mod/forum/db/access.php | 2 + theme/standard/styles_layout.css | 4 + version.php | 2 +- 13 files changed, 173 insertions(+), 96 deletions(-) diff --git a/admin/roles/allowoverride.php b/admin/roles/allowoverride.php index eacc00d653..2bc768f28c 100755 --- a/admin/roles/allowoverride.php +++ b/admin/roles/allowoverride.php @@ -74,7 +74,7 @@ $table->data[] = array_merge(array(format_string($role->name)), $beta); } - print_simple_box(get_string('configallowoverride', 'admin'), 'center'); + print_simple_box(get_string('configallowoverride2', 'admin'), 'center'); echo '
'; print_table($table); diff --git a/admin/roles/assign.php b/admin/roles/assign.php index 19715aea5f..afc98322ce 100755 --- a/admin/roles/assign.php +++ b/admin/roles/assign.php @@ -66,7 +66,6 @@ $overridableroles = get_overridable_roles($context, 'name', ROLENAME_BOTH); $assignableroles = get_assignable_roles($context, 'name', ROLENAME_BOTH); - /// Get some language strings $strpotentialusers = get_string('potentialusers', 'role'); @@ -108,7 +107,7 @@ /// Make sure this user can assign that role if ($roleid) { - if (!user_can_assign($context, $roleid)) { + if (!isset($assignableroles[$roleid])) { error ('you can not override this role in this context'); } } diff --git a/admin/roles/override.html b/admin/roles/override.html index 933ff538f3..74d0eafd75 100755 --- a/admin/roles/override.html +++ b/admin/roles/override.html @@ -3,6 +3,7 @@ $strallow = get_string('allow','role'); $strprevent = get_string('prevent','role'); $strprohibit = get_string('prohibit','role'); + $strsafewarning = get_string('safeoverridenotice', 'role'); ?> @@ -67,7 +68,7 @@ $isprohibit = 0; } - $isdisabled = $isprohibit; + $isdisabled = $isprohibit || $capability->locked; $riskinfo = ''; $rowclasses = ''; @@ -138,10 +139,17 @@ - +
+ + '.$strsafewarning.''; + } + ?> +
diff --git a/admin/roles/override.php b/admin/roles/override.php index bdd55f6056..fbb45ca6f0 100755 --- a/admin/roles/override.php +++ b/admin/roles/override.php @@ -20,7 +20,9 @@ print_error('cannotoverridebaserole', 'error'); } - if (!has_capability('moodle/role:override', $context)) { + $canoverride = has_capability('moodle/role:override', $context); + + if (!$canoverride and !has_capability('moodle/role:safeoverride', $context)) { print_error('nopermissions', 'error', '', 'change overrides in this context!'); } @@ -61,7 +63,7 @@ /// Make sure this user can override that role if ($roleid) { - if (!user_can_override($context, $roleid)) { + if (!isset($overridableroles[$roleid])) { error ('you can not override this role in this context'); } } @@ -72,7 +74,30 @@ } /// get all cababilities - $capabilities = fetch_context_capabilities($context); + $safeoverridenotice = false; + if ($roleid) { + if ($capabilities = fetch_context_capabilities($context)) { + // find out if we need to lock some capabilities + foreach ($capabilities as $capname=>$capability) { + $capabilities[$capname]->locked = false; + if ($canoverride) { + //ok no locking at all + continue; + } + //only limited safe overrides - spam only allowed + if ((RISK_DATALOSS & (int)$capability->riskbitmask) + or (RISK_MANAGETRUST & (int)$capability->riskbitmask) + or (RISK_CONFIG & (int)$capability->riskbitmask) + or (RISK_XSS & (int)$capability->riskbitmask) + or (RISK_PERSONAL & (int)$capability->riskbitmask)) { + $capabilities[$capname]->locked = true; + $safeoverridenotice = true; + } + } + } + } else { + $capabilities = null; + } /// Process incoming role override if ($data = data_submitted() and $roleid and confirm_sesskey()) { @@ -82,6 +107,10 @@ '', 'capability, permission, id'); foreach ($capabilities as $cap) { + if ($cap->locked) { + //user not allowed to change this cap + continue; + } if (!isset($data->{$cap->name})) { //cap not specified in form @@ -179,7 +208,7 @@ if (!empty($capabilities)) { // Print the capabilities overrideable in this context print_simple_box_start('center'); - include_once('override.html'); + include('override.html'); print_simple_box_end(); } else { diff --git a/blocks/admin/block_admin.php b/blocks/admin/block_admin.php index ebbd28fd9d..6a228a21d3 100644 --- a/blocks/admin/block_admin.php +++ b/blocks/admin/block_admin.php @@ -63,10 +63,14 @@ class block_admin extends block_list { /// Assign roles to the course - if ($course->id !== SITEID and has_capability('moodle/role:assign', $context)) { - $this->content->items[]=''.get_string('assignroles', 'role').''; - $this->content->icons[]=''; - + if ($course->id != SITEID) { + if (has_capability('moodle/role:assign', $context)) { + $this->content->items[]=''.get_string('assignroles', 'role').''; + $this->content->icons[]=''; + } else if (get_overridable_roles($context, 'name', ROLENAME_ORIGINAL)) { + $this->content->items[]=''.get_string('overridepermissions', 'role').''; + $this->content->icons[]=''; + } } /// View course grades (or just your own grades, same link) diff --git a/lang/en_utf8/admin.php b/lang/en_utf8/admin.php index 2adf02b2e3..081d3ab642 100644 --- a/lang/en_utf8/admin.php +++ b/lang/en_utf8/admin.php @@ -60,6 +60,7 @@ $string['configallowcoursethemes'] = 'If you enable this, then courses will be a $string['configallowemailaddresses'] = 'If you want to restrict all new email addresses to particular domains, then list them here separated by spaces. All other domains will be rejected. To allow subdomains add the domain with a preceding \'.\'. eg ourcollege.edu.au .gov.au'; $string['configallowobjectembed'] = 'As a default security measure, normal users are not allowed to embed multimedia (like Flash) within texts using explicit EMBED and OBJECT tags in their HTML (although it can still be done safely using the mediaplugins filter). If you wish to allow these tags then enable this option.'; $string['configallowoverride'] = 'You can allow people with the roles on the left side to override some of the column roles'; +$string['configallowoverride2'] = 'Select which role(s) can be overridden by each role in the left column.
Note that these settings only apply to users who have either the capability moodle/role:override or the capability moodle/role:safeoverride allowed.'; $string['configallowunenroll'] = 'If this is set \'Yes\', then students are allowed to unenrol themselves from courses whenever they like. Otherwise they are not allowed, and this process will be solely controlled by the teachers and administrators.'; $string['configallowuserblockhiding'] = 'Do you want to allow users to hide/show side blocks throughout this site? This feature uses Javascript and cookies to remember the state of each collapsible block, and only affects the user\'s own view.'; $string['configallowusermailcharset'] = 'Enabling this, every user in the site will be able to specify his own charset for email.'; diff --git a/lang/en_utf8/role.php b/lang/en_utf8/role.php index db55d19310..698bd08a95 100644 --- a/lang/en_utf8/role.php +++ b/lang/en_utf8/role.php @@ -124,6 +124,7 @@ $string['risks'] = 'Risks'; $string['role:assign'] = 'Assign roles to users'; $string['role:manage'] = 'Create and manage roles'; $string['role:override'] = 'Override permissions for others'; +$string['role:safeoverride'] = 'Override safe permissions for others'; $string['role:switchroles'] = 'Switch to other roles'; $string['role:unassignself'] = 'Unassign own roles'; $string['role:viewhiddenassigns'] = 'View hidden role assignments'; @@ -131,6 +132,7 @@ $string['roleassignments'] = 'Role assignments'; $string['roles'] = 'Roles'; $string['roletoassign'] = 'Role to assign'; $string['roletooverride'] = 'Role to override'; +$string['safeoverridenotice'] = 'Note: Capabilities with higher risks are locked because you are only allowed to override safe capabilities.'; $string['selectrole'] = 'Select a role'; $string['showallroles'] = 'Show all roles'; $string['site:accessallgroups'] = 'Access all groups'; diff --git a/lib/accesslib.php b/lib/accesslib.php index 7ca56187db..c85c4d6114 100755 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -149,6 +149,7 @@ define('RISK_CONFIG', 0x0002); define('RISK_XSS', 0x0004); define('RISK_PERSONAL', 0x0008); define('RISK_SPAM', 0x0010); +define('RISK_DATALOSS', 0x0020); // rolename displays define('ROLENAME_ORIGINAL', 0);// the name as defined in the role definition @@ -1756,7 +1757,7 @@ function moodle_install_roles() { allow_assign($editteacherrole, $studentrole); allow_assign($editteacherrole, $guestrole); -/// Set up default permissions for overrides +/// Set up default allow override matrix allow_override($adminrole, $adminrole); allow_override($adminrole, $coursecreatorrole); allow_override($adminrole, $noneditteacherrole); @@ -1764,6 +1765,11 @@ function moodle_install_roles() { allow_override($adminrole, $studentrole); allow_override($adminrole, $guestrole); allow_override($adminrole, $userrole); + + allow_override($editteacherrole, $noneditteacherrole); + allow_override($editteacherrole, $studentrole); + allow_override($editteacherrole, $guestrole); + } /** @@ -3815,6 +3821,9 @@ function get_user_roles_in_context($userid, $context, $view=true){ * @return boolean */ function user_can_override($context, $targetroleid) { + +// TODO: not needed anymore, remove in 2.0 + global $DB; // first check if user has override capability // if not return false; @@ -3954,45 +3963,33 @@ function allow_assign($sroleid, $troleid) { * Gets a list of roles that this user can assign in this context * @param object $context * @param string $field + * @param int $rolenamedisplay * @return array */ function get_assignable_roles($context, $field='name', $rolenamedisplay=ROLENAME_ALIAS) { - global $DB; - - // this users RAs - $ras = get_user_roles($context); - $roleids = array(); - foreach ($ras as $ra) { - $roleids[] = $ra->roleid; - } - unset($ra); + global $USER, $DB; - if (count($roleids)===0) { + if (!has_capability('moodle/role:assign', $context)) { return array(); - } - - list($roleids, $params) = $DB->get_in_or_equal($roleids, SQL_PARAMS_QM); - - // The subselect scopes the DISTINCT down to - // the role ids - a DISTINCT over the whole of - // the role table is much more expensive on some DBs - $sql = "SELECT r.id, r.$field - FROM {role} r - JOIN ( SELECT DISTINCT allowassign as allowedrole - FROM {role_allow_assign} raa - WHERE raa.roleid $roleids ) ar - ON r.id=ar.allowedrole - ORDER BY sortorder ASC"; - - if (!$rs = $DB->get_recordset_sql($sql, $params)) { + } + + $parents = get_parent_contexts($context); + $parents[] = $context->id; + $contexts = implode(',' , $parents); + + if (!$roles = $DB->get_records_sql("SELECT DISTINCT r.* + FROM {role} r, + {role_assignments} ra, + {role_allow_assign} raa + WHERE ra.userid = :userid AND ra.contextid IN ($contexts) + AND raa.roleid = ra.roleid AND r.id = raa.allowassign + ORDER BY r.sortorder ASC", array('userid'=>$USER->id))) { return array(); } - $roles = array(); - foreach ($rs as $r) { - $roles[$r->id] = $r->{$field}; + foreach ($roles as $role) { + $roles[$role->id] = $role->$field; } - $rs->close(); return role_fix_names($roles, $context, $rolenamedisplay); } @@ -4000,53 +3997,37 @@ function get_assignable_roles($context, $field='name', $rolenamedisplay=ROLENAME /** * Gets a list of roles that this user can assign in this context, for the switchrole menu * - * This is a quick-fix for MDL-13459 until MDL-8312 is sorted out... * @param object $context * @param string $field + * @param int $rolenamedisplay * @return array */ function get_assignable_roles_for_switchrole($context, $field='name', $rolenamedisplay=ROLENAME_ALIAS) { - global $DB; - - // this users RAs - $ras = get_user_roles($context); - $roleids = array(); - foreach ($ras as $ra) { - $roleids[] = $ra->roleid; - } - unset($ra); + global $USER, $DB; - if (count($roleids)===0) { + if (!has_capability('moodle/role:assign', $context)) { return array(); - } - - list($roleids, $params) = $DB->get_in_or_equal($roleids, SQL_PARAMS_QM); - - // The subselect scopes the DISTINCT down to - // the role ids - a DISTINCT over the whole of - // the role table is much more expensive on some DBs - $sql = "SELECT r.id, r.$field - FROM {role} r - JOIN ( SELECT DISTINCT allowassign as allowedrole - FROM {role_allow_assign} raa - WHERE raa.roleid $roleids ) ar - ON r.id=ar.allowedrole - JOIN {role_capabilities} rc - ON (r.id = rc.roleid AND rc.capability = ? - AND rc.capability != ?) - ORDER BY sortorder ASC"; - $params[] = 'moodle/course:view'; - $params[] = 'moodle/site:doanything'; - - if (!$rs = $DB->get_recordset_sql($sql, $params)) { + } + + $parents = get_parent_contexts($context); + $parents[] = $context->id; + $contexts = implode(',' , $parents); + + if (!$roles = $DB->get_records_sql("SELECT DISTINCT r.* + FROM {role} r, + {role_assignments} ra, + {role_allow_assign} raa, + {role_capabilities} rc + WHERE ra.userid = :userid AND ra.contextid IN ($contexts) + AND raa.roleid = ra.roleid AND r.id = raa.allowassign + AND r.id = rc.roleid AND rc.capability = :viewcap AND rc.capability <> :anythingcap + ORDER BY r.sortorder ASC", array('userid'=>$USER->id, 'viewcap'=>'moodle/course:view', 'anythingcap'=>'moodle/site:doanything'))) { return array(); } - $roles = array(); - foreach ($rs as $r) { - $roles[$r->id] = $r->{$field}; + foreach ($roles as $role) { + $roles[$role->id] = $role->$field; } - $rs->close(); return role_fix_names($roles, $context, $rolenamedisplay); } @@ -4054,21 +4035,36 @@ function get_assignable_roles_for_switchrole($context, $field='name', $rolenamed /** * Gets a list of roles that this user can override in this context * @param object $context + * @param string $field + * @param int $rolenamedisplay * @return array */ function get_overridable_roles($context, $field='name', $rolenamedisplay=ROLENAME_ALIAS) { + global $USER, $DB; - $options = array(); + if (!has_capability('moodle/role:override', $context) and !has_capability('moodle/role:safeoverride', $context)) { + return array(); + } + + $parents = get_parent_contexts($context); + $parents[] = $context->id; + $contexts = implode(',' , $parents); + + if (!$roles = $DB->get_records_sql("SELECT DISTINCT r.* + FROM {role} r, + {role_assignments} ra, + {role_allow_override} rao + WHERE ra.userid = :userid AND ra.contextid IN ($contexts) + AND rao.roleid = ra.roleid AND r.id = rao.allowoverride + ORDER BY r.sortorder ASC", array('userid'=>$USER->id))) { + return array(); + } - if ($roles = get_all_roles()) { - foreach ($roles as $role) { - if (user_can_override($context, $role->id)) { - $options[$role->id] = $role->$field; - } - } + foreach ($roles as $role) { + $roles[$role->id] = $role->$field; } - return role_fix_names($options, $context, $rolenamedisplay); + return role_fix_names($roles, $context, $rolenamedisplay); } /** @@ -4917,7 +4913,7 @@ function get_roles_on_exact_context($context) { * The caller *must* check * - that this op is allowed * - that the requested role can be assigned in this ctx - * (hint, use get_assignable_roles()) + * (hint, use get_assignable_roles_for_switchrole()) * - that the requested role is NOT $CFG->defaultuserroleid * * To "unswitch" pass 0 as the roleid. diff --git a/lib/db/access.php b/lib/db/access.php index 3bb9f05fbc..a4703ec421 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -123,7 +123,7 @@ $moodle_capabilities = array( 'moodle/site:sendmessage' => array( - 'riskbitmask' => RISK_PERSONAL, + 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, @@ -286,7 +286,7 @@ $moodle_capabilities = array( 'moodle/user:delete' => array( - 'riskbitmask' => RISK_PERSONAL, + 'riskbitmask' => RISK_PERSONAL, RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, @@ -345,6 +345,8 @@ $moodle_capabilities = array( 'moodle/role:assign' => array( + 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, + 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'legacy' => array( @@ -364,6 +366,17 @@ $moodle_capabilities = array( ) ), + 'moodle/role:safeoverride' => array( + + 'riskbitmask' => RISK_SPAM, + + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'editingteacher' => CAP_ALLOW + ) + ), + 'moodle/role:manage' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, @@ -401,7 +414,7 @@ $moodle_capabilities = array( 'moodle/role:switchroles' => array( - 'riskbitmask' => RISK_XSS, + 'riskbitmask' => RISK_XSS | RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, @@ -424,6 +437,8 @@ $moodle_capabilities = array( 'moodle/category:delete' => array( + 'riskbitmask' => RISK_DATALOSS, + 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, 'legacy' => array( @@ -465,6 +480,8 @@ $moodle_capabilities = array( 'moodle/course:delete' => array( + 'riskbitmask' => RISK_DATALOSS, + 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'legacy' => array( @@ -569,6 +586,8 @@ $moodle_capabilities = array( 'moodle/course:managemetacourse' => array( + 'riskbitmask' => RISK_XSS | RISK_PERSONAL, + 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'legacy' => array( @@ -653,6 +672,8 @@ $moodle_capabilities = array( 'moodle/course:reset' => array( + 'riskbitmask' => RISK_DATALOSS, + 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'legacy' => array( @@ -740,7 +761,7 @@ $moodle_capabilities = array( 'moodle/user:editprofile' => array( - 'riskbitmask' => RISK_SPAM, + 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_USER, @@ -751,6 +772,8 @@ $moodle_capabilities = array( 'moodle/user:editownprofile' => array( + 'riskbitmask' => RISK_SPAM, + 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'legacy' => array( @@ -1010,7 +1033,7 @@ $moodle_capabilities = array( ), 'moodle/grade:import' => array( - 'riskbitmask' => RISK_PERSONAL, + 'riskbitmask' => RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'legacy' => array( @@ -1033,7 +1056,7 @@ $moodle_capabilities = array( ), 'moodle/grade:manage' => array( - 'riskbitmask' => RISK_PERSONAL, + 'riskbitmask' => RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'legacy' => array( @@ -1120,6 +1143,8 @@ $moodle_capabilities = array( ), 'moodle/notes:manage' => array( + 'riskbitmask' => RISK_SPAM, + 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'legacy' => array( @@ -1130,6 +1155,8 @@ $moodle_capabilities = array( ), 'moodle/tag:manage' => array( + 'riskbitmask' => RISK_SPAM, + 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'legacy' => array( @@ -1140,6 +1167,8 @@ $moodle_capabilities = array( ), 'moodle/tag:create' => array( + 'riskbitmask' => RISK_SPAM, + 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'legacy' => array( @@ -1149,6 +1178,8 @@ $moodle_capabilities = array( ), 'moodle/tag:edit' => array( + 'riskbitmask' => RISK_SPAM, + 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'legacy' => array( diff --git a/mod/assignment/db/access.php b/mod/assignment/db/access.php index 0bce5db912..c4400b7ed4 100644 --- a/mod/assignment/db/access.php +++ b/mod/assignment/db/access.php @@ -56,6 +56,7 @@ $mod_assignment_capabilities = array( ), 'mod/assignment:grade' => array( + 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, diff --git a/mod/forum/db/access.php b/mod/forum/db/access.php index 1b4487eb19..961704a77e 100644 --- a/mod/forum/db/access.php +++ b/mod/forum/db/access.php @@ -241,6 +241,8 @@ $mod_forum_capabilities = array( 'mod/forum:managesubscriptions' => array( + 'riskbitmask' => RISK_SPAM, + 'captype' => 'read', 'contextlevel' => CONTEXT_MODULE, 'legacy' => array( diff --git a/theme/standard/styles_layout.css b/theme/standard/styles_layout.css index ed5aa90e77..951a369681 100644 --- a/theme/standard/styles_layout.css +++ b/theme/standard/styles_layout.css @@ -991,6 +991,10 @@ body#admin-modules table.generaltable td.c0 padding-top: 0.75em; } +#admin-roles-override .sefeoverridenotice { + text-align:center; +} + #admin-lang .generalbox { text-align:center; margin:auto; diff --git a/version.php b/version.php index 3e8db3e50c..ccdaf41522 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2008070701; // YYYYMMDD = date of the last version bump + $version = 2008072300; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20080723)'; // Human-friendly version name -- 2.39.5