MDL-62309 tool_policy: Improve permission evaluation methods

Please refer to the unit tests for the specification of the expected
behaviour.
This commit is contained in:
David Mudrák
2018-10-22 08:49:36 +02:00
parent 4a742e4f94
commit ad5e2135c5
10 changed files with 301 additions and 55 deletions
+86 -21
View File
@@ -780,14 +780,19 @@ class api {
}
/**
* Checks if user can accept policies for themselves or on behalf of another user
* Check if given policies can be accepted by the current user (eventually on behalf of the other user)
*
* @param int $userid
* @param bool $throwexception
* Currently, the version ids are not relevant and the check is based on permissions only. In the future, additional
* conditions can be added (such as policies applying to certain users only).
*
* @param array $versionids int[] List of policy version ids to check
* @param int $userid Accepting policies on this user's behalf (defaults to accepting on self)
* @param bool $throwexception Throw exception instead of returning false
* @return bool
*/
public static function can_accept_policies($userid = null, $throwexception = false) {
public static function can_accept_policies(array $versionids, $userid = null, $throwexception = false) {
global $USER;
if (!isloggedin() || isguestuser()) {
if ($throwexception) {
throw new \moodle_exception('noguest');
@@ -795,6 +800,7 @@ class api {
return false;
}
}
if (!$userid) {
$userid = $USER->id;
}
@@ -820,15 +826,48 @@ class api {
}
/**
* Checks if user can revoke policies for themselves or on behalf of another user
* Check if given policies can be declined by the current user (eventually on behalf of the other user)
*
* @param int $userid
* @param bool $throwexception
* Only optional policies can be declined. Otherwise, the permissions are same as for accepting policies.
*
* @param array $versionids int[] List of policy version ids to check
* @param int $userid Declining policies on this user's behalf (defaults to declining by self)
* @param bool $throwexception Throw exception instead of returning false
* @return bool
*/
public static function can_revoke_policies($userid = null, $throwexception = false) {
public static function can_decline_policies(array $versionids, $userid = null, $throwexception = false) {
foreach ($versionids as $versionid) {
if (static::get_agreement_optional($versionid) == policy_version::AGREEMENT_COMPULSORY) {
// Compulsory policies can't be declined (that is what makes them compulsory).
if ($throwexception) {
throw new \moodle_exception('errorpolicyversioncompulsory', 'tool_policy');
} else {
return false;
}
}
}
return static::can_accept_policies($versionids, $userid, $throwexception);
}
/**
* Check if acceptances to given policies can be revoked by the current user (eventually on behalf of the other user)
*
* Revoking optional policies is controlled by the same rules as declining them. Compulsory policies can be revoked
* only by users with the permission to accept policies on other's behalf. The reasoning behind this is to make sure
* the user communicates with the site's privacy officer and is well aware of all consequences of the decision (such
* as losing right to access the site).
*
* @param array $versionids int[] List of policy version ids to check
* @param int $userid Revoking policies on this user's behalf (defaults to revoking by self)
* @param bool $throwexception Throw exception instead of returning false
* @return bool
*/
public static function can_revoke_policies(array $versionids, $userid = null, $throwexception = false) {
global $USER;
// Guests' acceptance is not stored so there is nothing to revoke.
if (!isloggedin() || isguestuser()) {
if ($throwexception) {
throw new \moodle_exception('noguest');
@@ -836,20 +875,46 @@ class api {
return false;
}
}
if (!$userid) {
$userid = $USER->id;
// Sort policies into two sets according the optional flag.
$compulsory = [];
$optional = [];
foreach ($versionids as $versionid) {
$agreementoptional = static::get_agreement_optional($versionid);
if ($agreementoptional == policy_version::AGREEMENT_COMPULSORY) {
$compulsory[] = $versionid;
} else if ($agreementoptional == policy_version::AGREEMENT_OPTIONAL) {
$optional[] = $versionid;
} else {
throw new \coding_exception('Unexpected optional flag value');
}
}
// At the moment, current users can't revoke their own policies.
// Check capability to revoke on behalf as the real user.
$realuser = manager::get_realuser();
$usercontext = \context_user::instance($userid);
if ($throwexception) {
require_capability('tool/policy:acceptbehalf', $usercontext, $realuser);
return;
} else {
return has_capability('tool/policy:acceptbehalf', $usercontext, $realuser);
// Check if the user can revoke the optional policies from the list.
if ($optional) {
if (!static::can_decline_policies($optional, $userid, $throwexception)) {
return false;
}
}
// Check if the user can revoke the compulsory policies from the list.
if ($compulsory) {
if (!$userid) {
$userid = $USER->id;
}
$realuser = manager::get_realuser();
$usercontext = \context_user::instance($userid);
if ($throwexception) {
require_capability('tool/policy:acceptbehalf', $usercontext, $realuser);
return;
} else {
return has_capability('tool/policy:acceptbehalf', $usercontext, $realuser);
}
}
return true;
}
/**
@@ -897,7 +962,7 @@ class api {
if (!$userid) {
$userid = $USER->id;
}
self::can_accept_policies($userid, true);
self::can_accept_policies([$policyversionid], $userid, true);
// Retrieve the list of policy versions that need agreement (do not update existing agreements).
list($sql, $params) = $DB->get_in_or_equal($policyversionid, SQL_PARAMS_NAMED);
@@ -986,7 +1051,7 @@ class api {
if (!$userid) {
$userid = $USER->id;
}
self::can_accept_policies($userid, true);
self::can_accept_policies([$policyversionid], $userid, true);
if ($currentacceptance = $DB->get_record('tool_policy_acceptances',
['policyversionid' => $policyversionid, 'userid' => $userid])) {