diff --git a/admin/tool/dataprivacy/classes/api.php b/admin/tool/dataprivacy/classes/api.php index 2c18ac713d05e..80cb7ee172ec0 100644 --- a/admin/tool/dataprivacy/classes/api.php +++ b/admin/tool/dataprivacy/classes/api.php @@ -614,6 +614,12 @@ public static function approve_data_request($requestid) { throw new moodle_exception('errorrequestnotwaitingforapproval', 'tool_dataprivacy'); } + // Check if current user has permission to approve delete data request. + if ($request->get('type') == self::DATAREQUEST_TYPE_DELETE && !self::can_create_data_deletion_request_for_other()) { + throw new required_capability_exception(context_system::instance(), + 'tool/dataprivacy:requestdeleteforotheruser', 'nopermissions', ''); + } + // Update the status and the DPO. $result = self::update_request_status($requestid, self::DATAREQUEST_STATUS_APPROVED, $USER->id); @@ -653,6 +659,12 @@ public static function deny_data_request($requestid) { throw new moodle_exception('errorrequestnotwaitingforapproval', 'tool_dataprivacy'); } + // Check if current user has permission to reject delete data request. + if ($request->get('type') == self::DATAREQUEST_TYPE_DELETE && !self::can_create_data_deletion_request_for_other()) { + throw new required_capability_exception(context_system::instance(), + 'tool/dataprivacy:requestdeleteforotheruser', 'nopermissions', ''); + } + // Update the status and the DPO. return self::update_request_status($requestid, self::DATAREQUEST_STATUS_REJECTED, $USER->id); } @@ -752,6 +764,48 @@ public static function require_can_create_data_request_for_user($user, $requeste return true; } + /** + * Check if user has permisson to create data deletion request for themselves. + * + * @param int|null $userid ID of the user. + * @return bool + * @throws coding_exception + */ + public static function can_create_data_deletion_request_for_self(int $userid = null): bool { + global $USER; + $userid = $userid ?: $USER->id; + return has_capability('tool/dataprivacy:requestdelete', \context_user::instance($userid), $userid); + } + + /** + * Check if user has permission to create data deletion request for another user. + * + * @param int|null $userid ID of the user. + * @return bool + * @throws coding_exception + * @throws dml_exception + */ + public static function can_create_data_deletion_request_for_other(int $userid = null): bool { + global $USER; + $userid = $userid ?: $USER->id; + return has_capability('tool/dataprivacy:requestdeleteforotheruser', context_system::instance(), $userid); + } + + /** + * Check if parent can create data deletion request for their children. + * + * @param int $userid ID of a user being requested. + * @param int|null $requesterid ID of a user making request. + * @return bool + * @throws coding_exception + */ + public static function can_create_data_deletion_request_for_children(int $userid, int $requesterid = null): bool { + global $USER; + $requesterid = $requesterid ?: $USER->id; + return has_capability('tool/dataprivacy:makedatadeletionrequestsforchildren', \context_user::instance($userid), + $requesterid); + } + /** * Checks whether a user can download a data request. * diff --git a/admin/tool/dataprivacy/classes/external/data_request_exporter.php b/admin/tool/dataprivacy/classes/external/data_request_exporter.php index a309c8ed7dada..3c5ce09a04508 100644 --- a/admin/tool/dataprivacy/classes/external/data_request_exporter.php +++ b/admin/tool/dataprivacy/classes/external/data_request_exporter.php @@ -170,6 +170,10 @@ protected function get_other_values(renderer_base $output) { $values['canreview'] = true; // Whether the DPO can approve or deny the request. $values['approvedeny'] = in_array($requesttype, [api::DATAREQUEST_TYPE_EXPORT, api::DATAREQUEST_TYPE_DELETE]); + // If the request's type is delete, check if user have permission to approve/deny it. + if ($requesttype == api::DATAREQUEST_TYPE_DELETE) { + $values['approvedeny'] = api::can_create_data_deletion_request_for_other(); + } break; case api::DATAREQUEST_STATUS_APPROVED: $values['statuslabelclass'] = 'badge-info'; diff --git a/admin/tool/dataprivacy/classes/output/data_requests_table.php b/admin/tool/dataprivacy/classes/output/data_requests_table.php index b86e4642f6809..303c774622c07 100644 --- a/admin/tool/dataprivacy/classes/output/data_requests_table.php +++ b/admin/tool/dataprivacy/classes/output/data_requests_table.php @@ -116,9 +116,17 @@ public function __construct($userid = 0, $statuses = [], $types = [], $creationm * * @param stdClass $data The row data. * @return string + * @throws \moodle_exception + * @throws coding_exception */ public function col_select($data) { if ($data->status == \tool_dataprivacy\api::DATAREQUEST_STATUS_AWAITING_APPROVAL) { + if ($data->type == \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE + && !api::can_create_data_deletion_request_for_other()) { + // Don't show checkbox if request's type is delete and user don't have permission. + return false; + } + $stringdata = [ 'username' => $data->foruser->fullname, 'requesttype' => \core_text::strtolower($data->typenameshort) @@ -206,6 +214,7 @@ public function col_actions($data) { $requestid = $data->id; $status = $data->status; + $persistent = $this->datarequests[$requestid]; // Prepare actions. $actions = []; @@ -232,6 +241,11 @@ public function col_actions($data) { } break; case api::DATAREQUEST_STATUS_AWAITING_APPROVAL: + // Only show "Approve" and "Deny" button for deletion request if current user has permission. + if ($persistent->get('type') == api::DATAREQUEST_TYPE_DELETE && + !api::can_create_data_deletion_request_for_other()) { + break; + } // Approve. $actiondata['data-action'] = 'approve'; $actiontext = get_string('approverequest', 'tool_dataprivacy'); @@ -253,9 +267,11 @@ public function col_actions($data) { } if ($this->manage) { - $persistent = $this->datarequests[$requestid]; $canreset = $persistent->is_active() || empty($this->ongoingrequests[$data->foruser->id]->{$data->type}); $canreset = $canreset && $persistent->is_resettable(); + // Prevent re-submmit deletion request if current user don't have permission. + $canreset = $canreset && ($persistent->get('type') != api::DATAREQUEST_TYPE_DELETE || + api::can_create_data_deletion_request_for_other()); if ($canreset) { $reseturl = new moodle_url('/admin/tool/dataprivacy/resubmitrequest.php', [ 'requestid' => $requestid, diff --git a/admin/tool/dataprivacy/createdatarequest.php b/admin/tool/dataprivacy/createdatarequest.php index 12db2ea03e70b..b32dc15987972 100644 --- a/admin/tool/dataprivacy/createdatarequest.php +++ b/admin/tool/dataprivacy/createdatarequest.php @@ -76,6 +76,19 @@ } } + if ($data->type == \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE) { + if ($data->userid == $USER->id) { + if (!\tool_dataprivacy\api::can_create_data_deletion_request_for_self()) { + throw new moodle_exception('nopermissions', 'error', '', + get_string('errorcannotrequestdeleteforself', 'tool_dataprivacy')); + } + } else if (!\tool_dataprivacy\api::can_create_data_deletion_request_for_other() + && !\tool_dataprivacy\api::can_create_data_deletion_request_for_children($data->userid)) { + throw new moodle_exception('nopermissions', 'error', '', + get_string('errorcannotrequestdeleteforother', 'tool_dataprivacy')); + } + } + \tool_dataprivacy\api::create_data_request($data->userid, $data->type, $data->comments); if ($manage) { @@ -93,7 +106,7 @@ echo $OUTPUT->header(); echo $OUTPUT->heading($title); -echo $OUTPUT->box_start(); +echo $OUTPUT->box_start('createrequestform'); $mform->display(); echo $OUTPUT->box_end(); diff --git a/admin/tool/dataprivacy/createdatarequest_form.php b/admin/tool/dataprivacy/createdatarequest_form.php index 4c94a8a2a3979..c91213c67db6f 100644 --- a/admin/tool/dataprivacy/createdatarequest_form.php +++ b/admin/tool/dataprivacy/createdatarequest_form.php @@ -45,6 +45,7 @@ class tool_dataprivacy_data_request_form extends moodleform { * Form definition. * * @throws coding_exception + * @throws dml_exception */ public function definition() { global $USER; @@ -108,6 +109,24 @@ public function definition() { // Action buttons. $this->add_action_buttons(); + $shouldfreeze = false; + if ($this->manage) { + $shouldfreeze = !api::can_create_data_deletion_request_for_other(); + } else { + $shouldfreeze = !api::can_create_data_deletion_request_for_self(); + if ($shouldfreeze && !empty($useroptions)) { + foreach ($useroptions as $userid => $useroption) { + if (api::can_create_data_deletion_request_for_children($userid)) { + $shouldfreeze = false; + break; + } + } + } + } + + if ($shouldfreeze) { + $mform->freeze('type'); + } } /** @@ -120,6 +139,7 @@ public function definition() { * @throws dml_exception */ public function validation($data, $files) { + global $USER; $errors = []; $validrequesttypes = [ @@ -134,6 +154,19 @@ public function validation($data, $files) { $errors['type'] = get_string('errorrequestalreadyexists', 'tool_dataprivacy'); } + // Check if current user can create data deletion request. + $userid = $data['userid']; + if ($data['type'] == api::DATAREQUEST_TYPE_DELETE) { + if ($userid == $USER->id) { + if (!api::can_create_data_deletion_request_for_self()) { + $errors['type'] = get_string('errorcannotrequestdeleteforself', 'tool_dataprivacy'); + } + } else if (!api::can_create_data_deletion_request_for_other() + && !api::can_create_data_deletion_request_for_children($userid)) { + $errors['type'] = get_string('errorcannotrequestdeleteforother', 'tool_dataprivacy'); + } + } + return $errors; } } diff --git a/admin/tool/dataprivacy/db/access.php b/admin/tool/dataprivacy/db/access.php index ad3186702afaf..d70f4a572edaf 100644 --- a/admin/tool/dataprivacy/db/access.php +++ b/admin/tool/dataprivacy/db/access.php @@ -34,6 +34,15 @@ 'archetypes' => [] ], + // Capability for create new delete data request. Usually given to the site's Protection Officer. + 'tool/dataprivacy:requestdeleteforotheruser' => [ + 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_DATALOSS, + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'archetypes' => [], + 'clonepermissionsfrom' => 'tool/dataprivacy:managedatarequests' + ], + // Capability for managing the data registry. Usually given to the site's Data Protection Officer. 'tool/dataprivacy:managedataregistry' => [ 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_DATALOSS, @@ -50,6 +59,15 @@ 'archetypes' => [] ], + // Capability for parents/guardians to make delete data requests on behalf of their children. + 'tool/dataprivacy:makedatadeletionrequestsforchildren' => [ + 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, + 'captype' => 'write', + 'contextlevel' => CONTEXT_USER, + 'archetypes' => [], + 'clonepermissionsfrom' => 'tool/dataprivacy:makedatarequestsforchildren' + ], + // Capability for users to download the results of their own data request. 'tool/dataprivacy:downloadownrequest' => [ 'riskbitmask' => 0, @@ -67,4 +85,14 @@ 'contextlevel' => CONTEXT_USER, 'archetypes' => [] ], + + // Capability for users to create delete data request for their own. + 'tool/dataprivacy:requestdelete' => [ + 'riskbitmask' => RISK_DATALOSS, + 'captype' => 'write', + 'contextlevel' => CONTEXT_USER, + 'archetypes' => [ + 'user' => CAP_ALLOW + ] + ] ]; diff --git a/admin/tool/dataprivacy/lang/en/tool_dataprivacy.php b/admin/tool/dataprivacy/lang/en/tool_dataprivacy.php index bb3fa70953394..5a0efd41ae915 100644 --- a/admin/tool/dataprivacy/lang/en/tool_dataprivacy.php +++ b/admin/tool/dataprivacy/lang/en/tool_dataprivacy.php @@ -70,6 +70,7 @@ $string['contextpurposecategorysaved'] = 'Purpose and category saved.'; $string['contactdpoviaprivacypolicy'] = 'Please contact the privacy officer as described in the privacy policy.'; $string['createcategory'] = 'Create data category'; +$string['createdeletedatarequest'] = 'Create data deletion request'; $string['createnewdatarequest'] = 'Create a new data request'; $string['createpurpose'] = 'Create data purpose'; $string['creationauto'] = 'Automatically'; @@ -81,6 +82,9 @@ $string['dataprivacy:managedataregistry'] = 'Manage data registry'; $string['dataprivacy:downloadownrequest'] = 'Download your own exported data'; $string['dataprivacy:downloadallrequests'] = 'Download exported data for everyone'; +$string['dataprivacy:requestdeleteforotheruser'] = 'Request data deletion on behalf of another user'; +$string['dataprivacy:makedatadeletionrequestsforchildren'] = 'Request data deletion for minors'; +$string['dataprivacy:requestdelete'] = 'Request data deletion for yourself'; $string['dataregistry'] = 'Data registry'; $string['dataregistryinfo'] = 'The data registry enables categories (types of data) and purposes (the reasons for processing data) to be set for all content on the site - from users and courses down to activities and blocks. For each purpose, a retention period may be set. When a retention period has expired, the data is flagged and listed for deletion, awaiting admin confirmation.'; $string['dataretentionexplanation'] = 'This summary shows the default categories and purposes for retaining user data. Certain areas may have more specific categories and purposes than those listed here.'; @@ -124,6 +128,8 @@ $string['effectiveretentionperiodcourse'] = '{$a} (after the course end date)'; $string['effectiveretentionperioduser'] = '{$a} (since the last time the user accessed the site)'; $string['emailsalutation'] = 'Dear {$a},'; +$string['errorcannotrequestdeleteforself'] = 'You don\'t have permission to create deletion request for yourself.'; +$string['errorcannotrequestdeleteforother'] = 'You don\'t have permission to create deletion request for this user.'; $string['errorinvalidrequestcreationmethod'] = 'Invalid request creation method!'; $string['errorinvalidrequeststatus'] = 'Invalid request status!'; $string['errorinvalidrequesttype'] = 'Invalid request type!'; diff --git a/admin/tool/dataprivacy/lib.php b/admin/tool/dataprivacy/lib.php index 8655f1ca19429..c069301860693 100644 --- a/admin/tool/dataprivacy/lib.php +++ b/admin/tool/dataprivacy/lib.php @@ -77,8 +77,9 @@ function tool_dataprivacy_myprofile_navigation(tree $tree, $user, $iscurrentuser // Check if the user has an ongoing data deletion request. $hasdeleterequest = \tool_dataprivacy\api::has_ongoing_request($user->id, \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE); - // Show data deletion link only if the user doesn't have an ongoing data deletion request. - if (!$hasdeleterequest) { + // Show data deletion link only if the user doesn't have an ongoing data deletion request and has permission + // to create data deletion request. + if (!$hasdeleterequest && \tool_dataprivacy\api::can_create_data_deletion_request_for_self()) { $deleteparams = ['type' => \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE]; $deleteurl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php', $deleteparams); $deletenode = new core_user\output\myprofile\node('privacyandpolicies', 'requestdatadeletion', diff --git a/admin/tool/dataprivacy/resubmitrequest.php b/admin/tool/dataprivacy/resubmitrequest.php index ac7520e4ba8b9..4f3ef552d6120 100644 --- a/admin/tool/dataprivacy/resubmitrequest.php +++ b/admin/tool/dataprivacy/resubmitrequest.php @@ -44,6 +44,11 @@ ]; if (null !== $confirm && confirm_sesskey()) { + if ($originalrequest->get('type') == \tool_dataprivacy\api::DATAREQUEST_TYPE_DELETE + && !\tool_dataprivacy\api::can_create_data_deletion_request_for_other()) { + throw new required_capability_exception(context_system::instance(), + 'tool/dataprivacy:requestdeleteforotheruser', 'nopermissions', ''); + } $originalrequest->resubmit_request(); redirect($manageurl, get_string('resubmittedrequest', 'tool_dataprivacy', $stringparams)); } diff --git a/admin/tool/dataprivacy/tests/api_test.php b/admin/tool/dataprivacy/tests/api_test.php index 8e60223b96e92..f95dedda43934 100644 --- a/admin/tool/dataprivacy/tests/api_test.php +++ b/admin/tool/dataprivacy/tests/api_test.php @@ -2109,4 +2109,90 @@ protected function create_request_with_type_and_status(int $userid, int $type, i return $request; } + + /** + * Test user cannot create data deletion request for themselves if they don't have + * "tool/dataprivacy:requestdelete" capability. + * + * @throws coding_exception + */ + public function test_can_create_data_deletion_request_for_self_no() { + $this->resetAfterTest(); + $userid = $this->getDataGenerator()->create_user()->id; + $roleid = $this->getDataGenerator()->create_role(); + assign_capability('tool/dataprivacy:requestdelete', CAP_PROHIBIT, $roleid, context_user::instance($userid)); + role_assign($roleid, $userid, context_user::instance($userid)); + $this->setUser($userid); + $this->assertFalse(api::can_create_data_deletion_request_for_self()); + } + + /** + * Test user can create data deletion request for themselves if they have + * "tool/dataprivacy:requestdelete" capability. + * + * @throws coding_exception + */ + public function test_can_create_data_deletion_request_for_self_yes() { + $this->resetAfterTest(); + $userid = $this->getDataGenerator()->create_user()->id; + $this->setUser($userid); + $this->assertTrue(api::can_create_data_deletion_request_for_self()); + } + + /** + * Test user cannot create data deletion request for another user if they + * don't have "tool/dataprivacy:requestdeleteforotheruser" capability. + * + * @throws coding_exception + * @throws dml_exception + */ + public function test_can_create_data_deletion_request_for_other_no() { + $this->resetAfterTest(); + $userid = $this->getDataGenerator()->create_user()->id; + $this->setUser($userid); + $this->assertFalse(api::can_create_data_deletion_request_for_other()); + } + + /** + * Test user can create data deletion request for another user if they + * don't have "tool/dataprivacy:requestdeleteforotheruser" capability. + * + * @throws coding_exception + */ + public function test_can_create_data_deletion_request_for_other_yes() { + $this->resetAfterTest(); + $userid = $this->getDataGenerator()->create_user()->id; + $roleid = $this->getDataGenerator()->create_role(); + $contextsystem = context_system::instance(); + assign_capability('tool/dataprivacy:requestdeleteforotheruser', CAP_ALLOW, $roleid, $contextsystem); + role_assign($roleid, $userid, $contextsystem); + $this->setUser($userid); + $this->assertTrue(api::can_create_data_deletion_request_for_other($userid)); + } + + /** + * Check parents can create data deletion request for their children but not others. + * + * @throws coding_exception + * @throws dml_exception + */ + public function test_can_create_data_deletion_request_for_children() { + $this->resetAfterTest(); + + $parent = $this->getDataGenerator()->create_user(); + $child = $this->getDataGenerator()->create_user(); + $otheruser = $this->getDataGenerator()->create_user(); + + $contextsystem = \context_system::instance(); + $parentrole = $this->getDataGenerator()->create_role(); + assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, + $parentrole, $contextsystem); + assign_capability('tool/dataprivacy:makedatadeletionrequestsforchildren', CAP_ALLOW, + $parentrole, $contextsystem); + role_assign($parentrole, $parent->id, \context_user::instance($child->id)); + + $this->setUser($parent); + $this->assertTrue(api::can_create_data_deletion_request_for_children($child->id)); + $this->assertFalse(api::can_create_data_deletion_request_for_children($otheruser->id)); + } } diff --git a/admin/tool/dataprivacy/tests/behat/datadelete.feature b/admin/tool/dataprivacy/tests/behat/datadelete.feature index e6c7fde619668..5806102f6cc17 100644 --- a/admin/tool/dataprivacy/tests/behat/datadelete.feature +++ b/admin/tool/dataprivacy/tests/behat/datadelete.feature @@ -6,18 +6,24 @@ Feature: Data delete from the privacy API Background: Given the following "users" exist: - | username | firstname | lastname | - | victim | Victim User | 1 | - | parent | Long-suffering | Parent | + | username | firstname | lastname | + | victim | Victim User | 1 | + | parent | Long-suffering | Parent | + | privacyofficer | Privacy Officer | One | And the following "roles" exist: | shortname | name | archetype | | tired | Tired | | And the following "permission overrides" exist: - | capability | permission | role | contextlevel | reference | - | tool/dataprivacy:makedatarequestsforchildren | Allow | tired | System | | + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:makedatarequestsforchildren | Allow | tired | System | | + | tool/dataprivacy:makedatadeletionrequestsforchildren | Allow | tired | System | | + | tool/dataprivacy:managedatarequests | Allow | manager | System | | And the following "role assigns" exist: | user | role | contextlevel | reference | | parent | tired | User | victim | + And the following "system role assigns" exist: + | user | role | contextlevel | + | privacyofficer | manager | User | And the following config values are set as admin: | contactdataprotectionofficer | 1 | tool_dataprivacy | And the following data privacy "categories" exist: @@ -26,6 +32,10 @@ Feature: Data delete from the privacy API And the following data privacy "purposes" exist: | name | retentionperiod | | Site purpose | P10Y | + And the following config values are set as admin: + | contactdataprotectionofficer | 1 | tool_dataprivacy | + | privacyrequestexpiry | 55 | tool_dataprivacy | + | dporoles | 1 | tool_dataprivacy | And I set the site category and purpose to "Site category" and "Site purpose" @javascript @@ -115,3 +125,112 @@ Feature: Data delete from the privacy API And I run all adhoc tasks And I reload the page And I should see "You don't have any personal data requests" + + @javascript + Scenario: As a Privacy Officer, I cannot create data deletion request unless I have permission. + Given I log in as "privacyofficer" + And I navigate to "Users > Privacy and policies > Data requests" in site administration + And I follow "New request" + And I open the autocomplete suggestions list + And I click on "Victim User 1" item in the autocomplete list + Then I should see "Export all of my personal data" + And "Type" "select" should not be visible + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:requestdeleteforotheruser | Allow | manager | System | | + And I reload the page + And I open the autocomplete suggestions list + And I click on "Victim User 1" item in the autocomplete list + And "Type" "select" should be visible + + @javascript + Scenario: As a student, I cannot create data deletion request unless I have permission. + Given I log in as "victim" + And I follow "Profile" in the user menu + And I follow "Data requests" + And I follow "New request" + Then "Type" "select" should exist + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:requestdelete | Prevent | user | System | | + And I reload the page + And I should see "Export all of my personal data" + And "Type" "select" should not exist + + @javascript + Scenario: As a parent, I cannot create data deletion request unless I have permission. + Given I log in as "parent" + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:makedatadeletionrequestsforchildren | Prevent | tired | System | victim | + And I follow "Profile" in the user menu + And I follow "Data requests" + And I follow "New request" + And I open the autocomplete suggestions list + And I click on "Victim User 1" item in the autocomplete list + And I set the field "Type" to "Delete all of my personal data" + And I press "Save changes" + And I should see "You don't have permission to create deletion request for this user." + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:makedatadeletionrequestsforchildren | Allow | tired | System | victim | + | tool/dataprivacy:requestdelete | Prevent | user | System | | + And I open the autocomplete suggestions list + And I click on "Long-suffering Parent" item in the autocomplete list + And I press "Save changes" + And I should see "You don't have permission to create deletion request for yourself." + + @javascript + Scenario: As a student, link to create data deletion should not be shown if I don't have permission. + Given the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:requestdelete | Prohibit | user | System | | + When I log in as "victim" + And I follow "Profile" in the user menu + Then I should not see "Delete my account" + + @javascript + Scenario: As a Privacy Officer, I cannot Approve to Deny deletion data request without permission. + Given the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:requestdeleteforotheruser | Allow | manager | System | | + When I log in as "privacyofficer" + And I navigate to "Users > Privacy and policies > Data requests" in site administration + And I follow "New request" + And I open the autocomplete suggestions list + And I click on "Victim User 1" item in the autocomplete list + And I set the field "Type" to "Delete all of my personal data" + And I press "Save changes" + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:requestdeleteforotheruser | Prohibit | manager | System | | + And I reload the page + Then ".selectrequests" "css_element" should not exist + And I open the action menu in "region-main" "region" + And I should not see "Approve request" + And I should not see "Deny request" + And I choose "View the request" in the open action menu + And "Approve" "button" should not exist + And "Deny" "button" should not exist + + @javascript + Scenario: As a Privacy Officer, I cannot re-submit deletion data request without permission. + Given the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:requestdeleteforotheruser | Allow | manager | System | | + When I log in as "privacyofficer" + And I navigate to "Users > Privacy and policies > Data requests" in site administration + And I follow "New request" + And I open the autocomplete suggestions list + And I click on "Victim User 1" item in the autocomplete list + And I set the field "Type" to "Delete all of my personal data" + And I press "Save changes" + And I open the action menu in "region-main" "region" + And I follow "Deny request" + And I press "Deny request" + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | tool/dataprivacy:requestdeleteforotheruser | Prohibit | manager | System | | + And I reload the page + And I open the action menu in "region-main" "region" + Then I should not see "Resubmit as new request" diff --git a/admin/tool/dataprivacy/version.php b/admin/tool/dataprivacy/version.php index 411f0acfbbae4..8089b41f21ee1 100644 --- a/admin/tool/dataprivacy/version.php +++ b/admin/tool/dataprivacy/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2019011500; +$plugin->version = 2019040800; $plugin->requires = 2018112800; // Moodle 3.5dev (Build 2018031600) and upwards. $plugin->component = 'tool_dataprivacy';