예제 #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 context $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 $context, $user = null, $doanything = true)
{
    global $USER, $CFG, $SCRIPT, $ACCESSLIB_PRIVATE;
    if (during_initial_install()) {
        if ($SCRIPT === "/{$CFG->admin}/index.php" or $SCRIPT === "/{$CFG->admin}/cli/install.php" or $SCRIPT === "/{$CFG->admin}/cli/install_database.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!');
    }
    if (!is_bool($doanything)) {
        throw new coding_exception('Capability parameter "doanything" is wierd, only true or false is allowed. This has to be fixed in code.');
    }
    // capability must exist
    if (!($capinfo = get_capability_info($capability))) {
        debugging('Capability "' . $capability . '" was not found! This has to be fixed in code.');
        return false;
    }
    if (!isset($USER->id)) {
        // should never happen
        $USER->id = 0;
    }
    // make sure there is a real user specified
    if ($user === null) {
        $userid = $USER->id;
    } else {
        $userid = is_object($user) ? $user->id : $user;
    }
    // make sure forcelogin cuts off not-logged-in users if enabled
    if (!empty($CFG->forcelogin) and $userid == 0) {
        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 $capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS)) {
        if (isguestuser($userid) or $userid == 0) {
            return false;
        }
    }
    // somehow make sure the user is not deleted and actually exists
    if ($userid != 0) {
        if ($userid == $USER->id and isset($USER->deleted)) {
            // this prevents one query per page, it is a bit of cheating,
            // but hopefully session is terminated properly once user is deleted
            if ($USER->deleted) {
                return false;
            }
        } else {
            if (!context_user::instance($userid, IGNORE_MISSING)) {
                // no user context == invalid userid
                return false;
            }
        }
    }
    // context path/depth must be valid
    if (empty($context->path) or $context->depth == 0) {
        // this should not happen often, each upgrade tries to rebuild the context paths
        debugging('Context id ' . $context->id . ' does not have valid path, please use build_context_path()');
        if (is_siteadmin($userid)) {
            return true;
        } else {
            return false;
        }
    }
    // 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
        }
    }
    // Careful check for staleness...
    $context->reload_if_dirty();
    if ($USER->id == $userid) {
        if (!isset($USER->access)) {
            load_all_capabilities();
        }
        $access =& $USER->access;
    } else {
        // make sure user accessdata is really loaded
        get_user_accessdata($userid, true);
        $access =& $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
    }
    // Load accessdata for below-the-course context if necessary,
    // all contexts at and above all courses are already loaded
    if ($context->contextlevel != CONTEXT_COURSE and $coursecontext = $context->get_course_context(false)) {
        load_course_context($userid, $coursecontext, $access);
    }
    return has_capability_in_accessdata($capability, $context, $access);
}