diff --git a/admin/tool/dataprivacy/classes/expired_contexts_manager.php b/admin/tool/dataprivacy/classes/expired_contexts_manager.php index fa63eb0f671..90e7b2a730d 100644 --- a/admin/tool/dataprivacy/classes/expired_contexts_manager.php +++ b/admin/tool/dataprivacy/classes/expired_contexts_manager.php @@ -890,6 +890,11 @@ class expired_contexts_manager { * @return bool */ public static function is_context_expired_or_unprotected_for_user(\context $context, \stdClass $user) : bool { + // User/course contexts can't expire if no purpose is set in the system context. + if (!data_registry::defaults_set()) { + return false; + } + $parents = $context->get_parent_contexts(true); foreach ($parents as $parent) { if ($parent instanceof \context_course) { diff --git a/admin/tool/dataprivacy/classes/task/process_data_request_task.php b/admin/tool/dataprivacy/classes/task/process_data_request_task.php index 08342632d45..d988ac34320 100644 --- a/admin/tool/dataprivacy/classes/task/process_data_request_task.php +++ b/admin/tool/dataprivacy/classes/task/process_data_request_task.php @@ -74,6 +74,13 @@ class process_data_request_task extends adhoc_task { return; } + // If no site purpose is defined, reject requests since they cannot be processed. + if (!\tool_dataprivacy\data_registry::defaults_set()) { + api::update_request_status($requestid, api::DATAREQUEST_STATUS_REJECTED); + mtrace('No site purpose defined. Request ' . $requestid . ' rejected.'); + return; + } + // Grab the manager. // We set an observer against it to handle failures. $manager = new \core_privacy\manager(); diff --git a/admin/tool/dataprivacy/tests/behat/datadelete.feature b/admin/tool/dataprivacy/tests/behat/datadelete.feature index c4bc1391b79..e6c7fde6196 100644 --- a/admin/tool/dataprivacy/tests/behat/datadelete.feature +++ b/admin/tool/dataprivacy/tests/behat/datadelete.feature @@ -20,6 +20,13 @@ Feature: Data delete from the privacy API | parent | tired | User | victim | And the following config values are set as admin: | contactdataprotectionofficer | 1 | tool_dataprivacy | + And the following data privacy "categories" exist: + | name | + | Site category | + And the following data privacy "purposes" exist: + | name | retentionperiod | + | Site purpose | P10Y | + And I set the site category and purpose to "Site category" and "Site purpose" @javascript Scenario: As admin, delete a user and their data diff --git a/admin/tool/dataprivacy/tests/behat/dataexport.feature b/admin/tool/dataprivacy/tests/behat/dataexport.feature index 2751695166c..aa5c46ad0bf 100644 --- a/admin/tool/dataprivacy/tests/behat/dataexport.feature +++ b/admin/tool/dataprivacy/tests/behat/dataexport.feature @@ -21,6 +21,13 @@ Feature: Data export from the privacy API And the following config values are set as admin: | contactdataprotectionofficer | 1 | tool_dataprivacy | | privacyrequestexpiry | 55 | tool_dataprivacy | + And the following data privacy "categories" exist: + | name | + | Site category | + And the following data privacy "purposes" exist: + | name | retentionperiod | + | Site purpose | P10Y | + And I set the site category and purpose to "Site category" and "Site purpose" @javascript Scenario: As admin, export data for a user and download it, unless it has expired diff --git a/admin/tool/dataprivacy/tests/expired_data_requests_test.php b/admin/tool/dataprivacy/tests/expired_data_requests_test.php index 8c5cb93037b..eec0bd5397c 100644 --- a/admin/tool/dataprivacy/tests/expired_data_requests_test.php +++ b/admin/tool/dataprivacy/tests/expired_data_requests_test.php @@ -23,7 +23,9 @@ */ use tool_dataprivacy\api; +use tool_dataprivacy\category; use tool_dataprivacy\data_request; +use tool_dataprivacy\purpose; defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -62,6 +64,9 @@ class tool_dataprivacy_expired_data_requests_testcase extends data_privacy_testc $dpouser = $this->getDataGenerator()->create_user(); $this->assign_site_dpo($dpouser); + // Set site purpose. + $this->create_system_purpose(); + // Set request expiry to 5 minutes. set_config('privacyrequestexpiry', 300, 'tool_dataprivacy'); @@ -127,6 +132,9 @@ class tool_dataprivacy_expired_data_requests_testcase extends data_privacy_testc $admin = get_admin(); $this->setAdminUser(); + // Set site purpose. + $this->create_system_purpose(); + // Create export request. $datarequest = api::create_data_request($admin->id, api::DATAREQUEST_TYPE_EXPORT); $requestid = $datarequest->get('id'); @@ -166,4 +174,28 @@ class tool_dataprivacy_expired_data_requests_testcase extends data_privacy_testc $result = data_request::is_expired($request); $this->assertTrue($result); } + + /** + * Create a site (system context) purpose and category. + * + * @return void + */ + protected function create_system_purpose() { + $purpose = new purpose(0, (object) [ + 'name' => 'Test purpose ' . rand(1, 1000), + 'retentionperiod' => 'P1D', + 'lawfulbases' => 'gdpr_art_6_1_a', + ]); + $purpose->create(); + + $cat = new category(0, (object) ['name' => 'Test category']); + $cat->create(); + + $record = (object) [ + 'purposeid' => $purpose->get('id'), + 'categoryid' => $cat->get('id'), + 'contextlevel' => CONTEXT_SYSTEM, + ]; + api::set_contextlevel($record); + } }