accesslib: get_user_access_bycontext() fetches overrides in subcontexts, intro aggr_roles_fromsess()

The main thing here is that overrides in subcontexts are
now read correctly (and still cheaply) into the access array.

Also

 - introducing aggr_roles_fromsess() with gives you a list of
   the relevant roles that affect the user in this context

 - we clear out $USER->access on login/logout

 - get_user_access_bycontext() gets a few optimisations too...
This commit is contained in:
martinlanghoff
2007-09-19 07:03:19 +00:00
parent e0376a6241
commit 018d4b52b7
+80 -29
View File
@@ -520,6 +520,37 @@ function has_cap_fromsess($capability, $context, $sess, $doanything) {
}
}
function aggr_roles_fromsess($context, $sess) {
$path = $context->path;
// build $contexts as a list of "paths" of the current
// contexts and parents with the order top-to-bottom
$contexts = array($path);
while (preg_match('!^(/.+)/\d+$!', $path, $matches)) {
$path = $matches[1];
array_unshift($contexts, $path);
}
// Add a "default" context for the "default role"
array_unshift($contexts,"$path:def");
$cc = count($contexts);
$roles = array();
// From the bottom up...
for ($n=$cc-1;$n>=0;$n--) {
$ctxp = $contexts[$n];
if (isset($sess['ra'][$ctxp])) {
// Found a role assignment
$roleid = $sess['ra'][$ctxp];
$roles[] = $roleid;
}
}
return array_unique($roles);
}
/**
* This function checks for a capability assertion being true. If it isn't
* then the page is terminated neatly with a standard error message
@@ -1042,6 +1073,7 @@ function load_user_capability($capability='', $context=NULL, $userid=NULL, $chec
return false;
}
unset($USER->capabilities); // We don't want possible older capabilites hanging around
unset($USER->access);
if ($checkenrolments) { // Call "enrol" system to ensure that we have the correct picture
check_enrolment_plugins($USER);
@@ -1396,9 +1428,13 @@ function get_user_access_sitewide($userid) {
WHERE ra.userid = $userid AND ctx.contextlevel <= ".CONTEXT_COURSE."
ORDER BY ctx.depth, ctx.path";
$rs = get_recordset_sql($sql);
// parent paths & roles we need to walk up
// this array will bulk up quite a bit with dups
//
// raparents collects paths & roles we need to walk up
// the parenthood to build the rdef
//
// the array will bulk up a bit with dups
// which we'll later clear up
//
$raparents = array();
if ($rs->RecordCount()) {
while ($ra = rs_fetch_next_record($rs)) {
@@ -1481,8 +1517,6 @@ function get_user_access_sitewide($userid) {
}
rs_close($rs);
// TODO: compact capsets?
return $acc;
}
@@ -1499,18 +1533,24 @@ function get_user_access_bycontext($userid, $context, $acc=NULL) {
global $CFG;
/* Get in 3 cheap DB queries...
/* Get the additional RAs and relevant rolecaps
* - role assignments - with role_caps
* - relevant role caps
* - above this user's RAs
* - below this user's RAs - limited to course level
*/
// Roles already in use in this context
$knownroles = array();
if (is_null($acc)) {
$acc = array(); // named list
$acc['ra'] = array();
$acc['rdef'] = array();
$acc['loaded'] = array();
} else {
$knownroles = aggr_roles_fromsess($context, $acc);
}
$base = "/" . SYSCONTEXTID;
@@ -1560,10 +1600,18 @@ function get_user_access_bycontext($userid, $context, $acc=NULL) {
ORDER BY ctx.depth, ctx.path";
$rs = get_recordset_sql($sql);
// parent paths & roles we need to walk up
// this array will bulk up quite a bit with dups
//
// raparent collects paths & roles we need to walk up
//
// Here we only collect "different" role assignments
// that - if found - we have to walk up the parenthood
// to build the rdef.
//
// raparents array might have a few duplicates
// which we'll later clear up
//
$raparents = array();
$newroles = array();
if ($rs->RecordCount()) {
while ($ra = rs_fetch_next_record($rs)) {
$acc['ra'][$ra->path] = $ra->roleid;
@@ -1571,23 +1619,29 @@ function get_user_access_bycontext($userid, $context, $acc=NULL) {
$k = "{$ra->path}:{$ra->roleid}";
$acc['rdef'][$k][$ra->capability] = $ra->permission;
}
$parentids = explode('/', $ra->path);
array_pop($parentids); array_shift($parentids);
if (isset($raparents[$ra->roleid])) {
$raparents[$ra->roleid] = array_merge($raparents[$ra->roleid], $parentids);
} else {
$raparents[$ra->roleid] = $parentids;
if (!in_array($ra->roleid, $knownroles)) {
$newroles[] = $ra->roleid;
$parentids = explode('/', $ra->path);
array_pop($parentids); array_shift($parentids);
if (isset($raparents[$ra->roleid])) {
$raparents[$ra->roleid] = array_merge($raparents[$ra->roleid], $parentids);
} else {
$raparents[$ra->roleid] = $parentids;
}
}
}
$newroles = array_unique($newroles);
}
rs_close($rs);
//
// Walk up the tree to grab all the roledefs
// of interest to our user...
// NOTE: we use a series of IN clauses here - which
// might explode on huge sites with very convoluted nesting of
// categories... - extremely unlikely that the number of categories
// and roletypes is so large that we hit the limits of IN()
//
if (count($raparents)) {
$clauses = array();
foreach ($raparents as $roleid=>$contexts) {
@@ -1616,24 +1670,21 @@ function get_user_access_bycontext($userid, $context, $acc=NULL) {
}
//
// Overrides for the role assignments IN SUBCONTEXTS
// Overrides for the relevant roles IN SUBCONTEXTS
//
// NOTE that the JOIN w sctx is with 3-way triangulation to
// catch overrides to the applicable role in any subcontext, based
// on the path field of the parent.
// NOTE that we use IN() but the number of roles is
// very limited.
//
$sql = "SELECT sctx.path, ra.roleid,
ctx.path AS parentpath,
rco.capability, rco.permission
FROM {$CFG->prefix}role_assignments ra
JOIN {$CFG->prefix}context ctx
ON ra.contextid=ctx.id
JOIN {$CFG->prefix}context sctx
ON (sctx.path LIKE ctx.path||'/%')
JOIN {$CFG->prefix}role_capabilities rco
ON (rco.roleid=ra.roleid AND rco.contextid=sctx.id)
WHERE ra.userid = $userid AND
ORDER BY sctx.depth, sctx.path, ra.roleid";
$roleids = sql_intarray_to_in(array_merge($newroles, $knownroles));
$sql = "SELECT ctx.path, rc.roleid,
rc.capability, rc.permission
FROM {$CFG->prefix}context ctx
JOIN {$CFG->prefix}role_capabilities rc
ON rc.contextid=ctx.id
WHERE ctx.path LIKE '{$targetpath}/%'
AND rc.roleid IN ($roleids)
ORDER BY ctx.depth, ctx.path, rc.roleid";
$rs = get_recordset_sql($sql);
if ($rs->RecordCount()) {
while ($rd = rs_fetch_next_record($rs)) {
$k = "{$rd->path}:{$rd->roleid}";