MDL-63496 tool_dataprivacy: Respect expiry with protected flag
This issue is a part of the MDL-62560 Epic.
This commit is contained in:
@@ -988,6 +988,7 @@ class api {
|
||||
*/
|
||||
public static function add_request_contexts_with_status(contextlist_collection $clcollection, int $requestid, int $status) {
|
||||
$request = new data_request($requestid);
|
||||
$user = \core_user::get_user($request->get('userid'));
|
||||
foreach ($clcollection as $contextlist) {
|
||||
// Convert the \core_privacy\local\request\contextlist into a contextlist persistent and store it.
|
||||
$clp = \tool_dataprivacy\contextlist::from_contextlist($contextlist);
|
||||
@@ -998,10 +999,14 @@ class api {
|
||||
foreach ($contextlist->get_contextids() as $contextid) {
|
||||
if ($request->get('type') == static::DATAREQUEST_TYPE_DELETE) {
|
||||
$context = \context::instance_by_id($contextid);
|
||||
if (($purpose = static::get_effective_context_purpose($context)) && !empty($purpose->get('protected'))) {
|
||||
$purpose = static::get_effective_context_purpose($context);
|
||||
|
||||
// Data can only be deleted from it if the context is either expired, or unprotected.
|
||||
if (!expired_contexts_manager::is_context_expired_or_unprotected_for_user($context, $user)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$context = new contextlist_context();
|
||||
$context->set('contextid', $contextid)
|
||||
->set('contextlistid', $contextlistid)
|
||||
@@ -1099,6 +1104,15 @@ class api {
|
||||
$contexts = [];
|
||||
}
|
||||
|
||||
if ($request->get('type') == static::DATAREQUEST_TYPE_DELETE) {
|
||||
$context = \context::instance_by_id($record->contextid);
|
||||
$purpose = static::get_effective_context_purpose($context);
|
||||
// Data can only be deleted from it if the context is either expired, or unprotected.
|
||||
if (!expired_contexts_manager::is_context_expired_or_unprotected_for_user($context, $foruser)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$contexts[] = $record->contextid;
|
||||
$lastcomponent = $record->component;
|
||||
}
|
||||
|
||||
@@ -311,44 +311,7 @@ class expired_contexts_manager {
|
||||
}));
|
||||
|
||||
if (!$shouldskip && $context instanceof \context_user) {
|
||||
// The context instanceid is the user's ID.
|
||||
if (isguestuser($context->instanceid) || is_siteadmin($context->instanceid)) {
|
||||
// This is an admin, or the guest and cannot be deleted.
|
||||
$shouldskip = true;
|
||||
}
|
||||
|
||||
if (!$shouldskip) {
|
||||
$courses = enrol_get_users_courses($context->instanceid, false, ['enddate']);
|
||||
$requireenddate = self::require_all_end_dates_for_user_deletion();
|
||||
|
||||
foreach ($courses as $course) {
|
||||
if (empty($course->enddate)) {
|
||||
// This course has no end date.
|
||||
if ($requireenddate) {
|
||||
// Course end dates are required, and this course has no end date.
|
||||
$shouldskip = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Course end dates are not required. The subsequent checks are pointless at this time so just
|
||||
// skip them.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($course->enddate >= time()) {
|
||||
// This course is still in the future.
|
||||
$shouldskip = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// This course has an end date which is in the past.
|
||||
if (!self::is_course_expired($course)) {
|
||||
// This course has not expired yet.
|
||||
$shouldskip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$shouldskip = !self::are_user_context_dependencies_expired($context);
|
||||
}
|
||||
|
||||
if ($shouldskip) {
|
||||
@@ -586,8 +549,9 @@ class expired_contexts_manager {
|
||||
$defaultexpired = static::has_expired($purpose->get('retentionperiod'), $comparisondate);
|
||||
}
|
||||
|
||||
return new expiry_info($defaultexpired, [], []);
|
||||
return new expiry_info($defaultexpired, $purpose->get('protected'), [], [], []);
|
||||
} else {
|
||||
$protectedroles = [];
|
||||
foreach ($overrides as $override) {
|
||||
if (static::has_expired($override->get('retentionperiod'), $comparisondate)) {
|
||||
// This role has expired.
|
||||
@@ -595,6 +559,10 @@ class expired_contexts_manager {
|
||||
} else {
|
||||
// This role has not yet expired.
|
||||
$unexpiredroles[] = $override->get('roleid');
|
||||
|
||||
if ($override->get('protected')) {
|
||||
$protectedroles[$override->get('roleid')] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,7 +575,7 @@ class expired_contexts_manager {
|
||||
$expiredroles = [];
|
||||
}
|
||||
|
||||
return new expiry_info($defaultexpired, $expiredroles, $unexpiredroles);
|
||||
return new expiry_info($defaultexpired, $purpose->get('protected'), $expiredroles, $unexpiredroles, $protectedroles);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,8 +696,8 @@ class expired_contexts_manager {
|
||||
// Find the list of expired role users.
|
||||
$expiredroleuserassignments = get_role_users($expiredroles, $context, true, 'ra.id, u.id AS userid', 'ra.id');
|
||||
$expiredroleusers = array_map(function($assignment) {
|
||||
return $assignment->userid;
|
||||
}, $expiredroleuserassignments);
|
||||
return $assignment->userid;
|
||||
}, $expiredroleuserassignments);
|
||||
}
|
||||
$expiredroleusers = array_unique($expiredroleusers);
|
||||
|
||||
@@ -739,8 +707,8 @@ class expired_contexts_manager {
|
||||
// Find the list of unexpired role users.
|
||||
$unexpiredroleuserassignments = get_role_users($unexpiredroles, $context, true, 'ra.id, u.id AS userid', 'ra.id');
|
||||
$unexpiredroleusers = array_map(function($assignment) {
|
||||
return $assignment->userid;
|
||||
}, $unexpiredroleuserassignments);
|
||||
return $assignment->userid;
|
||||
}, $unexpiredroleuserassignments);
|
||||
}
|
||||
$unexpiredroleusers = array_unique($unexpiredroleusers);
|
||||
|
||||
@@ -749,12 +717,6 @@ class expired_contexts_manager {
|
||||
$tofilter = array_filter($tofilter, function($userroles) use ($expiredroles) {
|
||||
// Each iteration contains the list of role assignment for a specific user.
|
||||
// All roles that the user holds must match those in the list of expired roles.
|
||||
if (count($userroles) === 1) {
|
||||
// Shortcut - only one role held which must be one of the expired roles.
|
||||
// TODO I think this is wrong.
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($userroles as $ra) {
|
||||
if (false === array_search($ra->roleid, $expiredroles)) {
|
||||
// This role was not found in the list of assignments.
|
||||
@@ -773,6 +735,27 @@ class expired_contexts_manager {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the supplied context has expired.
|
||||
*
|
||||
* @param \context $context
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_context_expired(\context $context) : bool {
|
||||
$parents = $context->get_parent_contexts(true);
|
||||
foreach ($parents as $parent) {
|
||||
if ($parent instanceof \context_course) {
|
||||
return self::is_course_context_expired($context);
|
||||
}
|
||||
|
||||
if ($parent instanceof \context_user) {
|
||||
return self::are_user_context_dependencies_expired($context);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the course has expired.
|
||||
*
|
||||
@@ -781,11 +764,149 @@ class expired_contexts_manager {
|
||||
*/
|
||||
protected static function is_course_expired(\stdClass $course) : bool {
|
||||
$context = \context_course::instance($course->id);
|
||||
|
||||
return self::is_course_context_expired($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the supplied course context has expired.
|
||||
*
|
||||
* @param \context_course $context
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_course_context_expired(\context_course $context) : bool {
|
||||
$expiryrecords = self::get_nested_expiry_info_for_courses($context->path);
|
||||
|
||||
return !empty($expiryrecords[$context->path]) && $expiryrecords[$context->path]->info->is_fully_expired();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the supplied user context's dependencies have expired.
|
||||
*
|
||||
* This checks whether courses have expired, and some other check, but does not check whether the user themself has expired.
|
||||
*
|
||||
* Although this seems unusual at first, each location calling this actually checks whether the user is elgible for
|
||||
* deletion, irrespective if they have actually expired.
|
||||
*
|
||||
* For example, a request to delete the user only cares about course dependencies and the user's lack of expiry
|
||||
* should not block their own request to be deleted; whilst the expiry eligibility check has already tested for the
|
||||
* user being expired.
|
||||
*
|
||||
* @param \context_user $context
|
||||
* @return bool
|
||||
*/
|
||||
protected static function are_user_context_dependencies_expired(\context_user $context) : bool {
|
||||
// The context instanceid is the user's ID.
|
||||
if (isguestuser($context->instanceid) || is_siteadmin($context->instanceid)) {
|
||||
// This is an admin, or the guest and cannot expire.
|
||||
return false;
|
||||
}
|
||||
|
||||
$courses = enrol_get_users_courses($context->instanceid, false, ['enddate']);
|
||||
$requireenddate = self::require_all_end_dates_for_user_deletion();
|
||||
|
||||
$expired = true;
|
||||
|
||||
foreach ($courses as $course) {
|
||||
if (empty($course->enddate)) {
|
||||
// This course has no end date.
|
||||
if ($requireenddate) {
|
||||
// Course end dates are required, and this course has no end date.
|
||||
$expired = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Course end dates are not required. The subsequent checks are pointless at this time so just
|
||||
// skip them.
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($course->enddate >= time()) {
|
||||
// This course is still in the future.
|
||||
$expired = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// This course has an end date which is in the past.
|
||||
if (!self::is_course_expired($course)) {
|
||||
// This course has not expired yet.
|
||||
$expired = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $expired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the supplied context has expired or unprotected for the specified user.
|
||||
*
|
||||
* @param \context $context
|
||||
* @param \stdClass $user
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_context_expired_or_unprotected_for_user(\context $context, \stdClass $user) : bool {
|
||||
$parents = $context->get_parent_contexts(true);
|
||||
foreach ($parents as $parent) {
|
||||
if ($parent instanceof \context_course) {
|
||||
return self::is_course_context_expired_or_unprotected_for_user($parent, $user);
|
||||
}
|
||||
|
||||
if ($parent instanceof \context_user) {
|
||||
return self::are_user_context_dependencies_expired($context);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the supplied course context has expired, or is unprotected.
|
||||
*
|
||||
* @param \context_course $context
|
||||
* @param \stdClass $user
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_course_context_expired_or_unprotected_for_user(\context_course $context, \stdClass $user) {
|
||||
$expiryrecords = self::get_nested_expiry_info_for_courses($context->path);
|
||||
|
||||
$info = $expiryrecords[$context->path]->info;
|
||||
if ($info->is_fully_expired()) {
|
||||
// This context is fully expired.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now perform user checks.
|
||||
$userroles = array_map(function($assignment) {
|
||||
return $assignment->roleid;
|
||||
}, get_user_roles($context, $user->id));
|
||||
|
||||
$unexpiredprotectedroles = $info->get_unexpired_protected_roles();
|
||||
if (!empty(array_intersect($unexpiredprotectedroles, $userroles))) {
|
||||
// The user holds an unexpired and protected role.
|
||||
return false;
|
||||
}
|
||||
|
||||
$unprotectedoverriddenroles = $info->get_unprotected_overridden_roles();
|
||||
$matchingroles = array_intersect($unprotectedoverriddenroles, $userroles);
|
||||
if (!empty($matchingroles)) {
|
||||
// This user has at least one overridden role which is not a protected.
|
||||
// However, All such roles must match.
|
||||
// If the user has multiple roles then all must be expired, otherwise we should fall back to the default behaviour.
|
||||
if (empty(array_diff($userroles, $unprotectedoverriddenroles))) {
|
||||
// All roles that this user holds are a combination of expired, or unprotected.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($info->is_default_expired()) {
|
||||
// If the user has no unexpired roles, and the context is expired by default then this must be expired.
|
||||
return true;
|
||||
}
|
||||
|
||||
return !$info->is_default_protected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the privacy manager.
|
||||
*
|
||||
|
||||
@@ -41,23 +41,33 @@ class expiry_info {
|
||||
/** @var bool Whether the default expiry value of this purpose has been reached */
|
||||
protected $defaultexpiryreached = false;
|
||||
|
||||
/** @var bool Whether the default purpose is protected */
|
||||
protected $defaultprotected = false;
|
||||
|
||||
/** @var int[] List of expires roles */
|
||||
protected $expired = [];
|
||||
|
||||
/** @var int[] List of unexpires roles */
|
||||
protected $unexpired = [];
|
||||
|
||||
/** @var int[] List of unexpired roles which are also protected */
|
||||
protected $protectedroles = [];
|
||||
|
||||
/**
|
||||
* Constructor for the expiry_info class.
|
||||
*
|
||||
* @param bool $default Whether the default expiry period for this context has been reached.
|
||||
* @param bool $defaultprotected Whether the default expiry is protected.
|
||||
* @param int[] $expired A list of roles in this context which have explicitly expired.
|
||||
* @param int[] $unexpired A list of roles in this context which have not yet expired.
|
||||
* @param int[] $protectedroles A list of unexpired roles in this context which are protected.
|
||||
*/
|
||||
public function __construct(bool $default, array $expired, array $unexpired) {
|
||||
public function __construct(bool $default, bool $defaultprotected, array $expired, array $unexpired, array $protectedroles) {
|
||||
$this->defaultexpiryreached = $default;
|
||||
$this->defaultprotected = $defaultprotected;
|
||||
$this->expired = $expired;
|
||||
$this->unexpired = $unexpired;
|
||||
$this->protectedroles = $protectedroles;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +134,15 @@ class expiry_info {
|
||||
return $this->defaultexpiryreached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the default purpose is protected.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_default_protected() : bool {
|
||||
return $this->defaultprotected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of unexpired role IDs.
|
||||
*
|
||||
@@ -133,6 +152,25 @@ class expiry_info {
|
||||
return $this->unexpired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of unexpired protected roles.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function get_unexpired_protected_roles() : array {
|
||||
return array_keys(array_filter($this->protectedroles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all overridden roles which are unprotected.
|
||||
* @return int[]
|
||||
*/
|
||||
public function get_unprotected_overridden_roles() : array {
|
||||
$allroles = array_merge($this->expired, $this->unexpired);
|
||||
|
||||
return array_diff($allroles, $this->protectedroles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge this expiry_info object with another belonging to a child context in order to set the 'safest' heritage.
|
||||
*
|
||||
|
||||
@@ -24,11 +24,14 @@
|
||||
|
||||
use core\invalid_persistent_exception;
|
||||
use core\task\manager;
|
||||
use tool_dataprivacy\contextlist_context;
|
||||
use tool_dataprivacy\context_instance;
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\data_registry;
|
||||
use tool_dataprivacy\expired_context;
|
||||
use tool_dataprivacy\data_request;
|
||||
use tool_dataprivacy\purpose;
|
||||
use tool_dataprivacy\category;
|
||||
use tool_dataprivacy\local\helper;
|
||||
use tool_dataprivacy\task\initiate_data_request_task;
|
||||
use tool_dataprivacy\task\process_data_request_task;
|
||||
@@ -1376,6 +1379,228 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
|
||||
$this->assertEquals($data->contexts->used, $contextids, '', 0.0, 10, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that delete requests do not filter out protected purpose contexts if they are already expired.
|
||||
*/
|
||||
public function test_add_request_contexts_with_status_delete_course_expired_protected() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - YEARSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
$collection = new \core_privacy\local\request\contextlist_collection($user->id);
|
||||
$contextlist = new \core_privacy\local\request\contextlist();
|
||||
$contextlist->set_component('tool_dataprivacy');
|
||||
$contextlist->add_from_sql('SELECT id FROM {context} WHERE id IN(:ctx1)', ['ctx1' => $coursecontext->id]);
|
||||
$collection->add_contextlist($contextlist);
|
||||
|
||||
$request = api::create_data_request($user->id, api::DATAREQUEST_TYPE_DELETE);
|
||||
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
api::add_request_contexts_with_status($collection, $request->get('id'), contextlist_context::STATUS_APPROVED);
|
||||
|
||||
$requests = contextlist_context::get_records();
|
||||
$this->assertCount(1, $requests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that delete requests does filter out protected purpose contexts which are not expired.
|
||||
*/
|
||||
public function test_add_request_contexts_with_status_delete_course_unexpired_protected() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1Y');
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time()]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
$collection = new \core_privacy\local\request\contextlist_collection($user->id);
|
||||
$contextlist = new \core_privacy\local\request\contextlist();
|
||||
$contextlist->set_component('tool_dataprivacy');
|
||||
$contextlist->add_from_sql('SELECT id FROM {context} WHERE id IN(:ctx1)', ['ctx1' => $coursecontext->id]);
|
||||
$collection->add_contextlist($contextlist);
|
||||
|
||||
$request = api::create_data_request($user->id, api::DATAREQUEST_TYPE_DELETE);
|
||||
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
api::add_request_contexts_with_status($collection, $request->get('id'), contextlist_context::STATUS_APPROVED);
|
||||
|
||||
$requests = contextlist_context::get_records();
|
||||
$this->assertCount(0, $requests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that delete requests do not filter out unexpired contexts if they are not protected.
|
||||
*/
|
||||
public function test_add_request_contexts_with_status_delete_course_unexpired_unprotected() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1Y');
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time()]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
$collection = new \core_privacy\local\request\contextlist_collection($user->id);
|
||||
$contextlist = new \core_privacy\local\request\contextlist();
|
||||
$contextlist->set_component('tool_dataprivacy');
|
||||
$contextlist->add_from_sql('SELECT id FROM {context} WHERE id IN(:ctx1)', ['ctx1' => $coursecontext->id]);
|
||||
$collection->add_contextlist($contextlist);
|
||||
|
||||
$request = api::create_data_request($user->id, api::DATAREQUEST_TYPE_DELETE);
|
||||
|
||||
$purposes->course->set('protected', 0)->save();
|
||||
api::add_request_contexts_with_status($collection, $request->get('id'), contextlist_context::STATUS_APPROVED);
|
||||
|
||||
$requests = contextlist_context::get_records();
|
||||
$this->assertCount(1, $requests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that delete requests do not filter out protected purpose contexts if they are already expired.
|
||||
*/
|
||||
public function test_get_approved_contextlist_collection_for_request_delete_course_expired_protected() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - YEARSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
// Create the request, with its contextlist and context.
|
||||
$request = api::create_data_request($user->id, api::DATAREQUEST_TYPE_DELETE);
|
||||
$contextlist = new \tool_dataprivacy\contextlist(0, (object) ['component' => 'tool_dataprivacy']);
|
||||
$contextlist->save();
|
||||
|
||||
$clcontext = new \tool_dataprivacy\contextlist_context(0, (object) [
|
||||
'contextid' => $coursecontext->id,
|
||||
'status' => contextlist_context::STATUS_APPROVED,
|
||||
'contextlistid' => $contextlist->get('id'),
|
||||
]);
|
||||
$clcontext->save();
|
||||
|
||||
$rcl = new \tool_dataprivacy\request_contextlist(0, (object) [
|
||||
'requestid' => $request->get('id'),
|
||||
'contextlistid' => $contextlist->get('id'),
|
||||
]);
|
||||
$rcl->save();
|
||||
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
$collection = api::get_approved_contextlist_collection_for_request($request);
|
||||
|
||||
$this->assertCount(1, $collection);
|
||||
|
||||
$list = $collection->get_contextlist_for_component('tool_dataprivacy');
|
||||
$this->assertCount(1, $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that delete requests does filter out protected purpose contexts which are not expired.
|
||||
*/
|
||||
public function test_get_approved_contextlist_collection_for_request_delete_course_unexpired_protected() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1Y');
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time()]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
// Create the request, with its contextlist and context.
|
||||
$request = api::create_data_request($user->id, api::DATAREQUEST_TYPE_DELETE);
|
||||
$contextlist = new \tool_dataprivacy\contextlist(0, (object) ['component' => 'tool_dataprivacy']);
|
||||
$contextlist->save();
|
||||
|
||||
$clcontext = new \tool_dataprivacy\contextlist_context(0, (object) [
|
||||
'contextid' => $coursecontext->id,
|
||||
'status' => contextlist_context::STATUS_APPROVED,
|
||||
'contextlistid' => $contextlist->get('id'),
|
||||
]);
|
||||
$clcontext->save();
|
||||
|
||||
$rcl = new \tool_dataprivacy\request_contextlist(0, (object) [
|
||||
'requestid' => $request->get('id'),
|
||||
'contextlistid' => $contextlist->get('id'),
|
||||
]);
|
||||
$rcl->save();
|
||||
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
$collection = api::get_approved_contextlist_collection_for_request($request);
|
||||
|
||||
$this->assertCount(0, $collection);
|
||||
|
||||
$list = $collection->get_contextlist_for_component('tool_dataprivacy');
|
||||
$this->assertEmpty($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that delete requests do not filter out unexpired contexts if they are not protected.
|
||||
*/
|
||||
public function test_get_approved_contextlist_collection_for_request_delete_course_unexpired_unprotected() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1Y');
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time()]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
// Create the request, with its contextlist and context.
|
||||
$request = api::create_data_request($user->id, api::DATAREQUEST_TYPE_DELETE);
|
||||
$contextlist = new \tool_dataprivacy\contextlist(0, (object) ['component' => 'tool_dataprivacy']);
|
||||
$contextlist->save();
|
||||
|
||||
$clcontext = new \tool_dataprivacy\contextlist_context(0, (object) [
|
||||
'contextid' => $coursecontext->id,
|
||||
'status' => contextlist_context::STATUS_APPROVED,
|
||||
'contextlistid' => $contextlist->get('id'),
|
||||
]);
|
||||
$clcontext->save();
|
||||
|
||||
$rcl = new \tool_dataprivacy\request_contextlist(0, (object) [
|
||||
'requestid' => $request->get('id'),
|
||||
'contextlistid' => $contextlist->get('id'),
|
||||
]);
|
||||
$rcl->save();
|
||||
|
||||
$purposes->course->set('protected', 0)->save();
|
||||
$collection = api::get_approved_contextlist_collection_for_request($request);
|
||||
|
||||
$this->assertCount(1, $collection);
|
||||
|
||||
$list = $collection->get_contextlist_for_component('tool_dataprivacy');
|
||||
$this->assertCount(1, $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for \tool_dataprivacy_api_testcase::test_set_context_defaults
|
||||
*/
|
||||
@@ -1635,4 +1860,66 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
|
||||
'list' => $approvedcollection->get_contextlist_for_component('tool_dataprivacy'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the basics with the specified retention period.
|
||||
*
|
||||
* @param string $system Retention policy for the system.
|
||||
* @param string $user Retention policy for users.
|
||||
* @param string $course Retention policy for courses.
|
||||
* @param string $activity Retention policy for activities.
|
||||
*/
|
||||
protected function setup_basics(string $system, string $user, string $course = null, string $activity = null) : \stdClass {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = (object) [
|
||||
'system' => $this->create_and_set_purpose_for_contextlevel($system, CONTEXT_SYSTEM),
|
||||
'user' => $this->create_and_set_purpose_for_contextlevel($user, CONTEXT_USER),
|
||||
];
|
||||
|
||||
if (null !== $course) {
|
||||
$purposes->course = $this->create_and_set_purpose_for_contextlevel($course, CONTEXT_COURSE);
|
||||
}
|
||||
|
||||
if (null !== $activity) {
|
||||
$purposes->activity = $this->create_and_set_purpose_for_contextlevel($activity, CONTEXT_MODULE);
|
||||
}
|
||||
|
||||
return $purposes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a retention period and set it for the specified context level.
|
||||
*
|
||||
* @param string $retention
|
||||
* @param int $contextlevel
|
||||
* @return purpose
|
||||
*/
|
||||
protected function create_and_set_purpose_for_contextlevel(string $retention, int $contextlevel) : purpose {
|
||||
$purpose = new purpose(0, (object) [
|
||||
'name' => 'Test purpose ' . rand(1, 1000),
|
||||
'retentionperiod' => $retention,
|
||||
'lawfulbases' => 'gdpr_art_6_1_a',
|
||||
]);
|
||||
$purpose->create();
|
||||
|
||||
$cat = new category(0, (object) ['name' => 'Test category']);
|
||||
$cat->create();
|
||||
|
||||
if ($contextlevel <= CONTEXT_USER) {
|
||||
$record = (object) [
|
||||
'purposeid' => $purpose->get('id'),
|
||||
'categoryid' => $cat->get('id'),
|
||||
'contextlevel' => $contextlevel,
|
||||
];
|
||||
api::set_contextlevel($record);
|
||||
} else {
|
||||
list($purposevar, ) = data_registry::var_names_from_context(
|
||||
\context_helper::get_class_for_level(CONTEXT_COURSE)
|
||||
);
|
||||
set_config($purposevar, $purpose->get('id'), 'tool_dataprivacy');
|
||||
}
|
||||
|
||||
return $purpose;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ use tool_dataprivacy\purpose;
|
||||
use tool_dataprivacy\purpose_override;
|
||||
use tool_dataprivacy\category;
|
||||
use tool_dataprivacy\contextlevel;
|
||||
use tool_dataprivacy\expired_contexts_manager;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
@@ -50,15 +51,20 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
* @param string $course Retention policy for courses.
|
||||
* @param string $activity Retention policy for activities.
|
||||
*/
|
||||
protected function setup_basics(string $system, string $user, string $course, string $activity = null) : array {
|
||||
protected function setup_basics(string $system, string $user, string $course = null, string $activity = null) : \stdClass {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = [];
|
||||
$purposes[] = $this->create_and_set_purpose_for_contextlevel($system, CONTEXT_SYSTEM);
|
||||
$purposes[] = $this->create_and_set_purpose_for_contextlevel($user, CONTEXT_USER);
|
||||
$purposes[] = $this->create_and_set_purpose_for_contextlevel($course, CONTEXT_COURSE);
|
||||
$purposes = (object) [
|
||||
'system' => $this->create_and_set_purpose_for_contextlevel($system, CONTEXT_SYSTEM),
|
||||
'user' => $this->create_and_set_purpose_for_contextlevel($user, CONTEXT_USER),
|
||||
];
|
||||
|
||||
if (null !== $course) {
|
||||
$purposes->course = $this->create_and_set_purpose_for_contextlevel($course, CONTEXT_COURSE);
|
||||
}
|
||||
|
||||
if (null !== $activity) {
|
||||
$purposes[] = $this->create_and_set_purpose_for_contextlevel($activity, CONTEXT_MODULE);
|
||||
$purposes->activity = $this->create_and_set_purpose_for_contextlevel($activity, CONTEXT_MODULE);
|
||||
}
|
||||
|
||||
return $purposes;
|
||||
@@ -286,7 +292,6 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$userpurpose = $purposes[1];
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
@@ -295,7 +300,7 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||||
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $userpurpose->get('id'),
|
||||
'purposeid' => $purposes->user->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'P5Y',
|
||||
]);
|
||||
@@ -445,7 +450,7 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
public function test_flag_user_retention_changed() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
list($systempurpose, $userpurpose, $coursepurpose) = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
@@ -466,8 +471,8 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->assertNotFalse($expiredcontext);
|
||||
|
||||
// Increase the retention period to 5 years.
|
||||
$userpurpose->set('retentionperiod', 'P5Y');
|
||||
$userpurpose->save();
|
||||
$purposes->user->set('retentionperiod', 'P5Y');
|
||||
$purposes->user->save();
|
||||
|
||||
// Re-run the expiry job - the previously flagged user will be removed because the retention period has been increased.
|
||||
list($flaggedcourses, $flaggedusers) = $manager->flag_expired_contexts();
|
||||
@@ -485,7 +490,7 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
public function test_flag_user_historic_block_unapproved() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
list($systempurpose, $userpurpose, $coursepurpose) = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
@@ -522,8 +527,8 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
public function test_flag_user_historic_unexpired_child() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
list($systempurpose, $userpurpose, $coursepurpose) = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$blockpurpose = $this->create_and_set_purpose_for_contextlevel('P5Y', CONTEXT_BLOCK);
|
||||
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$this->create_and_set_purpose_for_contextlevel('P5Y', CONTEXT_BLOCK);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
@@ -680,12 +685,11 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$coursepurpose = $purposes[2];
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||||
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $coursepurpose->get('id'),
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'P5Y',
|
||||
]);
|
||||
@@ -720,13 +724,12 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$coursepurpose = $purposes[2];
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'student']);
|
||||
|
||||
// The role has a much shorter retention, but both should match.
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $coursepurpose->get('id'),
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'PT1M',
|
||||
]);
|
||||
@@ -760,12 +763,11 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('P1Y', 'P1Y', 'P1Y');
|
||||
$coursepurpose = $purposes[2];
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||||
|
||||
(new purpose_override(0, (object) [
|
||||
'purposeid' => $coursepurpose->get('id'),
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'PT1S',
|
||||
]))->save();
|
||||
@@ -830,7 +832,6 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$userpurpose = $purposes[1];
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
@@ -839,7 +840,7 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||||
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $userpurpose->get('id'),
|
||||
'purposeid' => $purposes->user->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'P5Y',
|
||||
]);
|
||||
@@ -889,12 +890,11 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$coursepurpose = $purposes[2];
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||||
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $coursepurpose->get('id'),
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'P5Y',
|
||||
]);
|
||||
@@ -978,12 +978,11 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P5Y');
|
||||
$coursepurpose = $purposes[2];
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'student']);
|
||||
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $coursepurpose->get('id'),
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'PT1M',
|
||||
]);
|
||||
@@ -1067,12 +1066,11 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P5Y');
|
||||
$coursepurpose = $purposes[2];
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'student']);
|
||||
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $coursepurpose->get('id'),
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'PT1M',
|
||||
]);
|
||||
@@ -1157,11 +1155,10 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P5Y');
|
||||
$coursepurpose = $purposes[2];
|
||||
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $coursepurpose->get('id'),
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $studentrole->id,
|
||||
'retentionperiod' => 'PT1M',
|
||||
]);
|
||||
@@ -1169,7 +1166,7 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
|
||||
$teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher']);
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $coursepurpose->get('id'),
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $teacherrole->id,
|
||||
'retentionperiod' => 'PT1M',
|
||||
]);
|
||||
@@ -1578,7 +1575,7 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
public function test_process_user_historic_block_unapproved() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
list($systempurpose, $userpurpose, $coursepurpose) = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
@@ -1637,8 +1634,8 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
public function test_process_user_historic_unexpired_child() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
list($systempurpose, $userpurpose, $coursepurpose) = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$blockpurpose = $this->create_and_set_purpose_for_contextlevel('P5Y', CONTEXT_BLOCK);
|
||||
$this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
$this->create_and_set_purpose_for_contextlevel('P5Y', CONTEXT_BLOCK);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - DAYSECS]);
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
@@ -1722,9 +1719,8 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$manager->set_progress(new \null_progress_trace());
|
||||
$manager->method('get_privacy_manager')->willReturn($mockprivacymanager);
|
||||
|
||||
$coursepurpose = $purposes[2];
|
||||
$coursepurpose->set('retentionperiod', 'P5Y');
|
||||
$coursepurpose->save();
|
||||
$purposes->course->set('retentionperiod', 'P5Y');
|
||||
$purposes->course->save();
|
||||
|
||||
list($processedcourses, $processedusers) = $manager->process_approved_deletions();
|
||||
|
||||
@@ -2185,4 +2181,387 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with the system context.
|
||||
*/
|
||||
public function test_is_context_expired_system() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired(\context_system::instance()));
|
||||
$this->assertFalse(
|
||||
expired_contexts_manager::is_context_expired_or_unprotected_for_user(\context_system::instance(), $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an expired course.
|
||||
*/
|
||||
public function test_is_context_expired_course_expired() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time()]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||||
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
$purposes->course->set('protected', 0)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an unexpired course.
|
||||
*/
|
||||
public function test_is_context_expired_course_unexpired() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired($coursecontext));
|
||||
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
$purposes->course->set('protected', 0)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an expired course which has role overrides.
|
||||
*/
|
||||
public function test_is_context_expired_course_expired_override() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'PT1H');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'P5Y',
|
||||
]);
|
||||
$override->save();
|
||||
role_assign($role->id, $user->id, $systemcontext->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||||
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
$purposes->course->set('protected', 0)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an expired course which has role overrides.
|
||||
*/
|
||||
public function test_is_context_expired_course_expired_override_parent() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $purposes->system->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'P5Y',
|
||||
]);
|
||||
$override->save();
|
||||
role_assign($role->id, $user->id, $systemcontext->id);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||||
|
||||
// The user override applies to this user. THIs means that the default expiry has no effect.
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
$purposes->system->set('protected', 1)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
$purposes->system->set('protected', 0)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
$override->set('protected', 1)->save();
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
$purposes->system->set('protected', 1)->save();
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
$purposes->system->set('protected', 0)->save();
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $user));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an expired course which has role overrides but the user
|
||||
* does not hold the role.
|
||||
*/
|
||||
public function test_is_context_expired_course_expired_override_parent_no_role() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $purposes->system->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'P5Y',
|
||||
]);
|
||||
$override->save();
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
// This context is not _fully _ expired.
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an unexpired course which has role overrides.
|
||||
*/
|
||||
public function test_is_context_expired_course_expired_override_inverse() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('P1Y', 'P1Y');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'student']);
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $purposes->system->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'PT1S',
|
||||
]);
|
||||
$override->save();
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
// This context is not _fully _ expired.
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an unexpired course which has role overrides.
|
||||
*/
|
||||
public function test_is_context_expired_course_expired_override_inverse_parent() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('P1Y', 'P1Y');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $purposes->system->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'PT1S',
|
||||
]);
|
||||
$override->save();
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
role_assign($role->id, $user->id, $systemcontext->id);
|
||||
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
role_unassign($studentrole->id, $user->id, $coursecontext->id);
|
||||
|
||||
// This context is not _fully _ expired.
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an unexpired course which has role overrides.
|
||||
*/
|
||||
public function test_is_context_expired_course_expired_override_inverse_parent_not_assigned() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('P1Y', 'P1Y');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - WEEKSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
|
||||
$role = $DB->get_record('role', ['shortname' => 'manager']);
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $purposes->system->get('id'),
|
||||
'roleid' => $role->id,
|
||||
'retentionperiod' => 'PT1S',
|
||||
]);
|
||||
$override->save();
|
||||
|
||||
// Enrol the user in the course without any role.
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
role_unassign($studentrole->id, $user->id, $coursecontext->id);
|
||||
|
||||
// This context is not _fully _ expired.
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($coursecontext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that context expired checks for a specific user taken into account roles.
|
||||
*/
|
||||
public function test_is_context_expired_or_unprotected_for_user_role_mixtures_protected() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1S', 'PT1S', 'PT1S');
|
||||
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - DAYSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
|
||||
$roles = $DB->get_records_menu('role', [], 'id', 'shortname, id');
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $roles['manager'],
|
||||
'retentionperiod' => 'P1W',
|
||||
'protected' => 1,
|
||||
]);
|
||||
$override->save();
|
||||
|
||||
$s = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($s->id, $course->id, 'student');
|
||||
|
||||
$t = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($t->id, $course->id, 'teacher');
|
||||
|
||||
$sm = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($sm->id, $course->id, 'student');
|
||||
role_assign($roles['manager'], $sm->id, $coursecontext->id);
|
||||
|
||||
$m = $this->getDataGenerator()->create_user();
|
||||
role_assign($roles['manager'], $m->id, $coursecontext->id);
|
||||
|
||||
$tm = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($t->id, $course->id, 'teacher');
|
||||
role_assign($roles['manager'], $tm->id, $coursecontext->id);
|
||||
|
||||
// The context should only be expired for users who are not a manager.
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $s));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $t));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $sm));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $tm));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $m));
|
||||
|
||||
$override->set('protected', 0)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $s));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $t));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $sm));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $tm));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $m));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that context expired checks for a specific user taken into account roles when retention is inversed.
|
||||
*/
|
||||
public function test_is_context_expired_or_unprotected_for_user_role_mixtures_protected_inverse() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('P5Y', 'P5Y', 'P5Y');
|
||||
|
||||
$course = $this->getDataGenerator()->create_course(['startdate' => time() - YEARSECS, 'enddate' => time() - DAYSECS]);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
|
||||
$roles = $DB->get_records_menu('role', [], 'id', 'shortname, id');
|
||||
$override = new purpose_override(0, (object) [
|
||||
'purposeid' => $purposes->course->get('id'),
|
||||
'roleid' => $roles['student'],
|
||||
'retentionperiod' => 'PT1S',
|
||||
]);
|
||||
$override->save();
|
||||
|
||||
$s = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($s->id, $course->id, 'student');
|
||||
|
||||
$t = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($t->id, $course->id, 'teacher');
|
||||
|
||||
$sm = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($sm->id, $course->id, 'student');
|
||||
role_assign($roles['manager'], $sm->id, $coursecontext->id);
|
||||
|
||||
$m = $this->getDataGenerator()->create_user();
|
||||
role_assign($roles['manager'], $m->id, $coursecontext->id);
|
||||
|
||||
$tm = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($t->id, $course->id, 'teacher');
|
||||
role_assign($roles['manager'], $tm->id, $coursecontext->id);
|
||||
|
||||
// The context should only be expired for users who are only a student.
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
$override->set('protected', 1)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $s));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $t));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $sm));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $tm));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $m));
|
||||
|
||||
$purposes->course->set('protected', 0)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $s));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $t));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $sm));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $tm));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($coursecontext, $m));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user