Ejemplo n.º 1
0
/**
 * Check whether a user has a particular capability in a given context.
 *
 * For example::
 *      $context = get_context_instance(CONTEXT_MODULE, $cm->id);
 *      has_capability('mod/forum:replypost',$context)
 *
 * By default checks the capabilities of the current user, but you can pass a
 * different userid. By default will return true for admin users, but you can override that with the fourth argument.
 *
 * Guest and not-logged-in users can never get any dangerous capability - that is any write capability
 * or capabilities with XSS, config or data loss risks.
 *
 * @param string $capability the name of the capability to check. For example mod/forum:view
 * @param object $context the context to check the capability in. You normally get this with {@link get_context_instance}.
 * @param integer|object $user A user id or object. By default (null) checks the permissions of the current user.
 * @param boolean $doanything If false, ignores effect of admin role assignment
 * @return boolean true if the user has this capability. Otherwise false.
 */
function has_capability($capability, $context, $user = null, $doanything = true)
{
    global $USER, $CFG, $DB, $SCRIPT, $ACCESSLIB_PRIVATE;
    if (during_initial_install()) {
        if ($SCRIPT === "/{$CFG->admin}/index.php" or $SCRIPT === "/{$CFG->admin}/cliupgrade.php") {
            // we are in an installer - roles can not work yet
            return true;
        } else {
            return false;
        }
    }
    if (strpos($capability, 'moodle/legacy:') === 0) {
        throw new coding_exception('Legacy capabilities can not be used any more!');
    }
    // the original $CONTEXT here was hiding serious errors
    // for security reasons do not reuse previous context
    if (empty($context)) {
        debugging('Incorrect context specified');
        return false;
    }
    if (!is_bool($doanything)) {
        throw new coding_exception('Capability parameter "doanything" is wierd ("' . $doanything . '"). This has to be fixed in code.');
    }
    // make sure there is a real user specified
    if ($user === null) {
        $userid = isset($USER->id) ? $USER->id : 0;
    } else {
        $userid = is_object($user) ? $user->id : $user;
    }
    // capability must exist
    if (!($capinfo = get_capability_info($capability))) {
        debugging('Capability "' . $capability . '" was not found! This should be fixed in code.');
        return false;
    }
    // make sure the guest account and not-logged-in users never get any risky caps no matter what the actual settings are.
    if ($capinfo->captype === 'write' or (int) $capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS)) {
        if (isguestuser($userid) or $userid == 0) {
            return false;
        }
    }
    if (is_null($context->path) or $context->depth == 0) {
        //this should not happen
        $contexts = array(SYSCONTEXTID, $context->id);
        $context->path = '/' . SYSCONTEXTID . '/' . $context->id;
        debugging('Context id ' . $context->id . ' does not have valid path, please use build_context_path()', DEBUG_DEVELOPER);
    } else {
        $contexts = explode('/', $context->path);
        array_shift($contexts);
    }
    if (CLI_SCRIPT && !isset($USER->access)) {
        // In cron, some modules setup a 'fake' $USER,
        // ensure we load the appropriate accessdata.
        if (isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
            $ACCESSLIB_PRIVATE->dirtycontexts = null;
            //load fresh dirty contexts
        } else {
            load_user_accessdata($userid);
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        }
        $USER->access = $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
    } else {
        if (isset($USER->id) && $USER->id == $userid && !isset($USER->access)) {
            // caps not loaded yet - better to load them to keep BC with 1.8
            // not-logged-in user or $USER object set up manually first time here
            load_all_capabilities();
            $ACCESSLIB_PRIVATE->accessdatabyuser = array();
            // reset the cache for other users too, the dirty contexts are empty now
            $ACCESSLIB_PRIVATE->roledefinitions = array();
        }
    }
    // Load dirty contexts list if needed
    if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
        if (isset($USER->access['time'])) {
            $ACCESSLIB_PRIVATE->dirtycontexts = get_dirty_contexts($USER->access['time']);
        } else {
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        }
    }
    // Careful check for staleness...
    if (count($ACCESSLIB_PRIVATE->dirtycontexts) !== 0 and is_contextpath_dirty($contexts, $ACCESSLIB_PRIVATE->dirtycontexts)) {
        // reload all capabilities - preserving loginas, roleswitches, etc
        // and then cleanup any marks of dirtyness... at least from our short
        // term memory! :-)
        $ACCESSLIB_PRIVATE->accessdatabyuser = array();
        $ACCESSLIB_PRIVATE->roledefinitions = array();
        if (CLI_SCRIPT) {
            load_user_accessdata($userid);
            $USER->access = $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        } else {
            reload_all_capabilities();
        }
    }
    // Find out if user is admin - it is not possible to override the doanything in any way
    // and it is not possible to switch to admin role either.
    if ($doanything) {
        if (is_siteadmin($userid)) {
            if ($userid != $USER->id) {
                return true;
            }
            // make sure switchrole is not used in this context
            if (empty($USER->access['rsw'])) {
                return true;
            }
            $parts = explode('/', trim($context->path, '/'));
            $path = '';
            $switched = false;
            foreach ($parts as $part) {
                $path .= '/' . $part;
                if (!empty($USER->access['rsw'][$path])) {
                    $switched = true;
                    break;
                }
            }
            if (!$switched) {
                return true;
            }
            //ok, admin switched role in this context, let's use normal access control rules
        }
    }
    // divulge how many times we are called
    //// error_log("has_capability: id:{$context->id} path:{$context->path} userid:$userid cap:$capability");
    if (isset($USER->id) && $USER->id == $userid) {
        // we must accept strings and integers in $userid
        //
        // For the logged in user, we have $USER->access
        // which will have all RAs and caps preloaded for
        // course and above contexts.
        //
        // Contexts below courses && contexts that do not
        // hang from courses are loaded into $USER->access
        // on demand, and listed in $USER->access[loaded]
        //
        if ($context->contextlevel <= CONTEXT_COURSE) {
            // Course and above are always preloaded
            return has_capability_in_accessdata($capability, $context, $USER->access);
        }
        // Load accessdata for below-the-course contexts
        if (!path_inaccessdata($context->path, $USER->access)) {
            // error_log("loading access for context {$context->path} for $capability at {$context->contextlevel} {$context->id}");
            // $bt = debug_backtrace();
            // error_log("bt {$bt[0]['file']} {$bt[0]['line']}");
            load_subcontext($USER->id, $context, $USER->access);
        }
        return has_capability_in_accessdata($capability, $context, $USER->access);
    }
    if (!isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
        load_user_accessdata($userid);
    }
    if ($context->contextlevel <= CONTEXT_COURSE) {
        // Course and above are always preloaded
        return has_capability_in_accessdata($capability, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]);
    }
    // Load accessdata for below-the-course contexts as needed
    if (!path_inaccessdata($context->path, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
        // error_log("loading access for context {$context->path} for $capability at {$context->contextlevel} {$context->id}");
        // $bt = debug_backtrace();
        // error_log("bt {$bt[0]['file']} {$bt[0]['line']}");
        load_subcontext($userid, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]);
    }
    return has_capability_in_accessdata($capability, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]);
}
Ejemplo n.º 2
0
/**
 * This function returns whether the current user has the capability of performing a function
 * For example, we can do has_capability('mod/forum:replypost',$context) in forum
 * @param string $capability - name of the capability (or debugcache or clearcache)
 * @param object $context - a context object (record from context table)
 * @param integer $userid - a userid number, empty if current $USER
 * @param bool $doanything - if false, ignore do anything
 * @return bool
 */
function has_capability($capability, $context, $userid = NULL, $doanything = true)
{
    global $USER, $CFG, $DB, $SCRIPT, $ACCESSLIB_PRIVATE;
    if (empty($CFG->rolesactive)) {
        if ($SCRIPT === "/{$CFG->admin}/index.php" or $SCRIPT === "/{$CFG->admin}/cliupgrade.php") {
            // we are in an installer - roles can not work yet
            return true;
        } else {
            return false;
        }
    }
    // the original $CONTEXT here was hiding serious errors
    // for security reasons do not reuse previous context
    if (empty($context)) {
        debugging('Incorrect context specified');
        return false;
    }
    /// Some sanity checks
    if (debugging('', DEBUG_DEVELOPER)) {
        if (!is_valid_capability($capability)) {
            debugging('Capability "' . $capability . '" was not found! This should be fixed in code.');
        }
        if (!is_bool($doanything)) {
            debugging('Capability parameter "doanything" is wierd ("' . $doanything . '"). This should be fixed in code.');
        }
    }
    if (empty($userid)) {
        // we must accept null, 0, '0', '' etc. in $userid
        $userid = $USER->id;
    }
    if (is_null($context->path) or $context->depth == 0) {
        //this should not happen
        $contexts = array(SYSCONTEXTID, $context->id);
        $context->path = '/' . SYSCONTEXTID . '/' . $context->id;
        debugging('Context id ' . $context->id . ' does not have valid path, please use build_context_path()', DEBUG_DEVELOPER);
    } else {
        $contexts = explode('/', $context->path);
        array_shift($contexts);
    }
    if (CLI_SCRIPT && !isset($USER->access)) {
        // In cron, some modules setup a 'fake' $USER,
        // ensure we load the appropriate accessdata.
        if (isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
            $ACCESSLIB_PRIVATE->dirtycontexts = NULL;
            //load fresh dirty contexts
        } else {
            load_user_accessdata($userid);
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        }
        $USER->access = $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
    } else {
        if ($USER->id == $userid && !isset($USER->access)) {
            // caps not loaded yet - better to load them to keep BC with 1.8
            // not-logged-in user or $USER object set up manually first time here
            load_all_capabilities();
            $ACCESSLIB_PRIVATE->accessdatabyuser = array();
            // reset the cache for other users too, the dirty contexts are empty now
            $ACCESSLIB_PRIVATE->roledefinitions = array();
        }
    }
    // Load dirty contexts list if needed
    if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
        if (isset($USER->access['time'])) {
            $ACCESSLIB_PRIVATE->dirtycontexts = get_dirty_contexts($USER->access['time']);
        } else {
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        }
    }
    // Careful check for staleness...
    if (count($ACCESSLIB_PRIVATE->dirtycontexts) !== 0 and is_contextpath_dirty($contexts, $ACCESSLIB_PRIVATE->dirtycontexts)) {
        // reload all capabilities - preserving loginas, roleswitches, etc
        // and then cleanup any marks of dirtyness... at least from our short
        // term memory! :-)
        $ACCESSLIB_PRIVATE->accessdatabyuser = array();
        $ACCESSLIB_PRIVATE->roledefinitions = array();
        if (CLI_SCRIPT) {
            load_user_accessdata($userid);
            $USER->access = $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        } else {
            reload_all_capabilities();
        }
    }
    // divulge how many times we are called
    //// error_log("has_capability: id:{$context->id} path:{$context->path} userid:$userid cap:$capability");
    if ($USER->id == $userid) {
        // we must accept strings and integers in $userid
        //
        // For the logged in user, we have $USER->access
        // which will have all RAs and caps preloaded for
        // course and above contexts.
        //
        // Contexts below courses && contexts that do not
        // hang from courses are loaded into $USER->access
        // on demand, and listed in $USER->access[loaded]
        //
        if ($context->contextlevel <= CONTEXT_COURSE) {
            // Course and above are always preloaded
            return has_capability_in_accessdata($capability, $context, $USER->access, $doanything);
        }
        // Load accessdata for below-the-course contexts
        if (!path_inaccessdata($context->path, $USER->access)) {
            // error_log("loading access for context {$context->path} for $capability at {$context->contextlevel} {$context->id}");
            // $bt = debug_backtrace();
            // error_log("bt {$bt[0]['file']} {$bt[0]['line']}");
            load_subcontext($USER->id, $context, $USER->access);
        }
        return has_capability_in_accessdata($capability, $context, $USER->access, $doanything);
    }
    if (!isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
        load_user_accessdata($userid);
    }
    if ($context->contextlevel <= CONTEXT_COURSE) {
        // Course and above are always preloaded
        return has_capability_in_accessdata($capability, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid], $doanything);
    }
    // Load accessdata for below-the-course contexts as needed
    if (!path_inaccessdata($context->path, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
        // error_log("loading access for context {$context->path} for $capability at {$context->contextlevel} {$context->id}");
        // $bt = debug_backtrace();
        // error_log("bt {$bt[0]['file']} {$bt[0]['line']}");
        load_subcontext($userid, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]);
    }
    return has_capability_in_accessdata($capability, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid], $doanything);
}
Ejemplo n.º 3
0
function has_capability($capability, $context, $userid = NULL, $doanything = true)
{
    global $USER, $ACCESS, $CFG, $DIRTYCONTEXTS;
    // the original $CONTEXT here was hiding serious errors
    // for security reasons do notreuse previous context
    if (empty($context)) {
        debugging('Incorrect context specified');
        return false;
    }
    if (is_null($userid) || $userid === 0) {
        $userid = $USER->id;
    }
    $contexts = array();
    $basepath = '/' . SYSCONTEXTID;
    if (empty($context->path)) {
        $contexts[] = SYSCONTEXTID;
        $context->path = $basepath;
        if (isset($context->id) && $context->id == !SYSCONTEXTID) {
            $contexts[] = $context->id;
            $context->path .= '/' . $context->id;
        }
    } else {
        $contexts = explode('/', $context->path);
        array_shift($contexts);
    }
    if ($USER->id === 0 && !isset($USER->access)) {
        // not-logged-in user first time here
        load_all_capabilities();
    } else {
        if (defined('FULLME') && FULLME === 'cron' && !isset($USER->access)) {
            //
            // In cron, some modules setup a 'fake' $USER,
            // ensure we load the appropriate accessdata.
            // Also: set $DIRTYCONTEXTS to empty
            //
            if (!isset($ACCESS)) {
                $ACCESS = array();
            }
            if (!isset($ACCESS[$userid])) {
                load_user_accessdata($userid);
            }
            $USER->access = $ACCESS[$userid];
            $DIRTYCONTEXTS = array();
        } else {
            if ($USER->id === $userid && !isset($USER->access)) {
                // caps not loaded yet - better to load them to keep BC with 1.8
                // probably $USER object set up manually
                load_all_capabilities();
            }
        }
    }
    // Careful check for staleness...
    $clean = true;
    if (!isset($DIRTYCONTEXTS)) {
        // Load dirty contexts list
        $DIRTYCONTEXTS = get_dirty_contexts($USER->access['time']);
        // Check basepath only once, when
        // we load the dirty contexts...
        if (isset($DIRTYCONTEXTS[$basepath])) {
            // sitewide change, dirty
            $clean = false;
        }
    }
    // Check for staleness in the whole parenthood
    if ($clean && !is_contextpath_clean($context->path, $DIRTYCONTEXTS)) {
        $clean = false;
    }
    if (!$clean) {
        // reload all capabilities - preserving loginas, roleswitches, etc
        // and then cleanup any marks of dirtyness... at least from our short
        // term memory! :-)
        reload_all_capabilities();
        $DIRTYCONTEXTS = array();
        $clean = true;
    }
    // divulge how many times we are called
    //// error_log("has_capability: id:{$context->id} path:{$context->path} userid:$userid cap:$capability");
    if ($USER->id === $userid) {
        //
        // For the logged in user, we have $USER->access
        // which will have all RAs and caps preloaded for
        // course and above contexts.
        //
        // Contexts below courses && contexts that do not
        // hang from courses are loaded into $USER->access
        // on demand, and listed in $USER->access[loaded]
        //
        if ($context->contextlevel <= CONTEXT_COURSE) {
            // Course and above are always preloaded
            return has_capability_in_accessdata($capability, $context, $USER->access, $doanything);
        }
        // Load accessdata for below-the-course contexts
        if (!path_inaccessdata($context->path, $USER->access)) {
            error_log("loading access for context {$context->path} for {$capability} at {$context->contextlevel} {$context->id}");
            // $bt = debug_backtrace();
            // error_log("bt {$bt[0]['file']} {$bt[0]['line']}");
            $USER->access = get_user_access_bycontext($USER->id, $context, $USER->access);
        }
        return has_capability_in_accessdata($capability, $context, $USER->access, $doanything);
    }
    if (!isset($ACCESS)) {
        $ACCESS = array();
    }
    if (!isset($ACCESS[$userid])) {
        load_user_accessdata($userid);
    }
    if ($context->contextlevel <= CONTEXT_COURSE) {
        // Course and above are always preloaded
        return has_capability_in_accessdata($capability, $context, $ACCESS[$userid], $doanything);
    }
    // Load accessdata for below-the-course contexts as needed
    if (!path_inaccessdata($context->path, $ACCESS[$userid])) {
        error_log("loading access for context {$context->path} for {$capability} at {$context->contextlevel} {$context->id}");
        // $bt = debug_backtrace();
        // error_log("bt {$bt[0]['file']} {$bt[0]['line']}");
        $ACCESS[$userid] = get_user_access_bycontext($userid, $context, $ACCESS[$userid]);
    }
    return has_capability_in_accessdata($capability, $context, $ACCESS[$userid], $doanything);
}