Skip to content

Commit

Permalink
MDL-31243 access/enrol libs: new fns returning useful SQL fragments
Browse files Browse the repository at this point in the history
Refactor similar SQL generation code from get_users_by_capability
and get_enrolled_uses to make get_with_capability_sql.
Modified files: accesslib.php, enrollib.php, grouplib.php, new tests.
  • Loading branch information
Beedell committed Sep 30, 2016
1 parent 9f5170e commit 9121bb2
Show file tree
Hide file tree
Showing 6 changed files with 469 additions and 157 deletions.
165 changes: 165 additions & 0 deletions lib/accesslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -7277,3 +7277,168 @@ function get_suspended_userids(context $context, $usecache = false) {

return $susers;
}

/**
* Gets sql for finding users with a capability in the given context
*
* @param context $context
* @param string $capability
* @return array($sql, $params)
*/
function get_with_capability_sql(context $context, $capability) {
static $i = 0;
$i++;
$prefix = 'cu' . $i . '_';

$capjoin = get_with_capability_join($context, $capability, $prefix . 'u.id');

$sql = "SELECT DISTINCT {$prefix}u.id
FROM {user} {$prefix}u
$capjoin->joins
WHERE {$prefix}u.deleted = 0 AND $capjoin->wheres";

return array($sql, $capjoin->params);
}

/**
* Gets sql joins for finding users with a capability in the given context
*
* @param context $context
* @param string $capability
* @param string $useridcolumn e.g. u.id
* @return \core\dml\sql_join Contains joins, wheres, params
*/
function get_with_capability_join(context $context, $capability, $useridcolumn) {
global $DB, $CFG;

// Use unique prefix just in case somebody makes some SQL magic with the result.
static $i = 0;
$i++;
$prefix = 'eu' . $i . '_';

// First find the course context.
$coursecontext = $context->get_course_context();

$isfrontpage = ($coursecontext->instanceid == SITEID);

$joins = array();
$wheres = array();
$params = array();

list($contextids, $contextpaths) = get_context_info_list($context);

list($incontexts, $cparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'ctx');
$cparams['cap'] = $capability;

$defs = array();
$sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path
FROM {role_capabilities} rc
JOIN {context} ctx on rc.contextid = ctx.id
WHERE rc.contextid $incontexts AND rc.capability = :cap";
$rcs = $DB->get_records_sql($sql, $cparams);
foreach ($rcs as $rc) {
$defs[$rc->path][$rc->roleid] = $rc->permission;
}

$access = array();
if (!empty($defs)) {
foreach ($contextpaths as $path) {
if (empty($defs[$path])) {
continue;
}
foreach ($defs[$path] as $roleid => $perm) {
if ($perm == CAP_PROHIBIT) {
$access[$roleid] = CAP_PROHIBIT;
continue;
}
if (!isset($access[$roleid])) {
$access[$roleid] = (int) $perm;
}
}
}
}

unset($defs);

// Make lists of roles that are needed and prohibited.
$needed = array(); // One of these is enough.
$prohibited = array(); // Must not have any of these.
foreach ($access as $roleid => $perm) {
if ($perm == CAP_PROHIBIT) {
unset($needed[$roleid]);
$prohibited[$roleid] = true;
} else {
if ($perm == CAP_ALLOW and empty($prohibited[$roleid])) {
$needed[$roleid] = true;
}
}
}

$defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0;
$defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0;

$nobody = false;

if ($isfrontpage) {
if (!empty($prohibited[$defaultuserroleid]) or !empty($prohibited[$defaultfrontpageroleid])) {
$nobody = true;
} else {
if (!empty($needed[$defaultuserroleid]) or !empty($needed[$defaultfrontpageroleid])) {
// Everybody not having prohibit has the capability.
$needed = array();
} else {
if (empty($needed)) {
$nobody = true;
}
}
}
} else {
if (!empty($prohibited[$defaultuserroleid])) {
$nobody = true;
} else {
if (!empty($needed[$defaultuserroleid])) {
// Everybody not having prohibit has the capability.
$needed = array();
} else {
if (empty($needed)) {
$nobody = true;
}
}
}
}

if ($nobody) {
// Nobody can match so return some SQL that does not return any results.
$wheres[] = "1 = 2";

} else {

if ($needed) {
$ctxids = implode(',', $contextids);
$roleids = implode(',', array_keys($needed));
$joins[] = "JOIN {role_assignments} {$prefix}ra3
ON ({$prefix}ra3.userid = $useridcolumn
AND {$prefix}ra3.roleid IN ($roleids)
AND {$prefix}ra3.contextid IN ($ctxids))";
}

if ($prohibited) {
$ctxids = implode(',', $contextids);
$roleids = implode(',', array_keys($prohibited));
$joins[] = "LEFT JOIN {role_assignments} {$prefix}ra4
ON ({$prefix}ra4.userid = $useridcolumn
AND {$prefix}ra4.roleid IN ($roleids)
AND {$prefix}ra4.contextid IN ($ctxids))";
$wheres[] = "{$prefix}ra4.id IS NULL";
}

}

$wheres[] = "$useridcolumn <> :{$prefix}guestid";
$params["{$prefix}guestid"] = $CFG->siteguest;

$joins = implode("\n", $joins);
$wheres = "(" . implode(" AND ", $wheres) . ")";

return new \core\dml\sql_join($joins, $wheres, $params);
}
69 changes: 69 additions & 0 deletions lib/classes/dml/sql_join.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* An object that contains sql join fragments.
*
* @since Moodle 3.1
* @package core
* @category dml
* @copyright 2016 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace core\dml;

defined('MOODLE_INTERNAL') || die();

/**
* An object that contains sql join fragments.
*
* @since Moodle 3.1
* @package core
* @category dml
* @copyright 2016 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sql_join {

/**
* @var string joins.
*/
public $joins;

/**
* @var string wheres.
*/
public $wheres;

/**
* @var array params.
*/
public $params;

/**
* Create an object that contains sql join fragments.
*
* @param string $joins The join sql fragment.
* @param string $wheres The where sql fragment.
* @param array $params Any parameter values.
*/
public function __construct($joins = '', $wheres = '', $params = array()) {
$this->joins = $joins;
$this->wheres = $wheres;
$this->params = $params;
}
}
Loading

0 comments on commit 9121bb2

Please sign in to comment.