Merge branch 'MDL-64322' of https://github.com/KietChan/moodle
authorAdrian Greeve <abgreeve@gmail.com>
Wed, 10 Apr 2019 05:23:54 +0000 (13:23 +0800)
committerAdrian Greeve <abgreeve@gmail.com>
Wed, 10 Apr 2019 05:23:54 +0000 (13:23 +0800)
12 files changed:
admin/tool/dataprivacy/classes/api.php
admin/tool/dataprivacy/classes/external/data_request_exporter.php
admin/tool/dataprivacy/classes/output/data_requests_table.php
admin/tool/dataprivacy/createdatarequest.php
admin/tool/dataprivacy/createdatarequest_form.php
admin/tool/dataprivacy/db/access.php
admin/tool/dataprivacy/lang/en/tool_dataprivacy.php
admin/tool/dataprivacy/lib.php
admin/tool/dataprivacy/resubmitrequest.php
admin/tool/dataprivacy/tests/api_test.php
admin/tool/dataprivacy/tests/behat/datadelete.feature
admin/tool/dataprivacy/version.php

index 2c18ac7..80cb7ee 100644 (file)
@@ -614,6 +614,12 @@ class api {
             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 @@ class api {
             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 @@ class api {
         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.
      *
index a309c8e..3c5ce09 100644 (file)
@@ -170,6 +170,10 @@ class data_request_exporter extends persistent_exporter {
                 $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';
index b86e464..303c774 100644 (file)
@@ -116,9 +116,17 @@ class data_requests_table extends table_sql {
      *
      * @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 @@ class data_requests_table extends table_sql {
 
         $requestid = $data->id;
         $status = $data->status;
+        $persistent = $this->datarequests[$requestid];
 
         // Prepare actions.
         $actions = [];
@@ -232,6 +241,11 @@ class data_requests_table extends table_sql {
                 }
                 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 @@ class data_requests_table extends table_sql {
         }
 
         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,
index 12db2ea..b32dc15 100644 (file)
@@ -76,6 +76,19 @@ if ($data = $mform->get_data()) {
         }
     }
 
+    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 @@ $PAGE->set_title($title);
 echo $OUTPUT->header();
 echo $OUTPUT->heading($title);
 
-echo $OUTPUT->box_start();
+echo $OUTPUT->box_start('createrequestform');
 $mform->display();
 echo $OUTPUT->box_end();
 
index 4c94a8a..c91213c 100644 (file)
@@ -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 @@ class tool_dataprivacy_data_request_form extends moodleform {
         // 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 @@ class tool_dataprivacy_data_request_form extends moodleform {
      * @throws dml_exception
      */
     public function validation($data, $files) {
+        global $USER;
         $errors = [];
 
         $validrequesttypes = [
@@ -134,6 +154,19 @@ class tool_dataprivacy_data_request_form extends moodleform {
             $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;
     }
 }
index ad31867..d70f4a5 100644 (file)
@@ -34,6 +34,15 @@ $capabilities = [
         '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 @@ $capabilities = [
         '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 @@ $capabilities = [
         '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
+        ]
+    ]
 ];
index bb3fa70..5a0efd4 100644 (file)
@@ -70,6 +70,7 @@ $string['contextlevelname80'] = 'Blocks';
 $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:managedatarequests'] = 'Manage data requests';
 $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['editpurposes'] = 'Edit purposes';
 $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!';
index 8655f1c..c069301 100644 (file)
@@ -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',
index ac7520e..4f3ef55 100644 (file)
@@ -44,6 +44,11 @@ $stringparams = (object) [
     ];
 
 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));
 }
index 8e60223..f95dedd 100644 (file)
@@ -2109,4 +2109,90 @@ class tool_dataprivacy_api_testcase extends advanced_testcase {
 
         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));
+    }
 }
index e6c7fde..5806102 100644 (file)
@@ -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"
index 411f0ac..8089b41 100644 (file)
@@ -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';