Rev 1 | AutorÃa | Comparar con el anterior | Ultima modificación | Ver Log |
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* External tests.
*
* @package tool_dataprivacy
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_dataprivacy\external;
use core_external\external_api;
use externallib_advanced_testcase;
use tool_dataprivacy\api;
use tool_dataprivacy\data_request;
use tool_dataprivacy\context_instance;
use tool_dataprivacy\external;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
/**
* External testcase.
*
* @package tool_dataprivacy
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class external_test extends externallib_advanced_testcase {
/**
* Test for external::approve_data_request() with the user not logged in.
*/
public function test_approve_data_request_not_logged_in(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
// Test data request creation.
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Log out the user and set force login to true.
$this->setUser();
$this->expectException(\require_login_exception::class);
external::approve_data_request($datarequest->get('id'));
}
/**
* Test for external::approve_data_request() with the user not having a DPO role.
*/
public function test_approve_data_request_not_dpo(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
// Test data request creation.
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Login as the requester.
$this->setUser($requester);
$this->expectException(\required_capability_exception::class);
external::approve_data_request($datarequest->get('id'));
}
/**
* Test for external::approve_data_request() for request that's not ready for approval
*/
public function test_approve_data_request_not_waiting_for_approval(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
// Test data request creation.
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
$datarequest->set('status', api::DATAREQUEST_STATUS_CANCELLED)->save();
// Admin as DPO. (The default when no one's assigned as a DPO in the site).
$this->setAdminUser();
$this->expectException(\moodle_exception::class);
external::approve_data_request($datarequest->get('id'));
}
/**
* Test for external::approve_data_request()
*/
public function test_approve_data_request(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
// Test data request creation.
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Admin as DPO. (The default when no one's assigned as a DPO in the site).
$this->setAdminUser();
api::update_request_status($datarequest->get('id'), api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
$result = external::approve_data_request($datarequest->get('id'));
$return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
$this->assertTrue($return->result);
$this->assertEmpty($return->warnings);
}
/**
* Test for external::approve_data_request() for a non-existent request ID.
*/
public function test_approve_data_request_non_existent(): void {
$this->resetAfterTest();
// Admin as DPO. (The default when no one's assigned as a DPO in the site).
$this->setAdminUser();
$result = external::approve_data_request(1);
$return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
$this->assertFalse($return->result);
$this->assertCount(1, $return->warnings);
$warning = reset($return->warnings);
$this->assertEquals('errorrequestnotfound', $warning['warningcode']);
}
/**
* Test for external::cancel_data_request() of another user.
*/
public function test_cancel_data_request_other_user(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$otheruser = $generator->create_user();
$comment = 'sample comment';
// Test data request creation.
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Login as other user.
$this->setUser($otheruser);
$result = external::cancel_data_request($datarequest->get('id'));
$return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
$this->assertFalse($return->result);
$this->assertCount(1, $return->warnings);
$warning = reset($return->warnings);
$this->assertEquals('errorrequestnotfound', $warning['warningcode']);
}
/**
* Test cancellation of a request where you are the requester of another user's data.
*/
public function test_cancel_data_request_other_user_as_requester(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$otheruser = $generator->create_user();
$comment = 'sample comment';
// Assign requester as otheruser'sparent.
$systemcontext = \context_system::instance();
$parentrole = $generator->create_role();
assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext);
role_assign($parentrole, $requester->id, \context_user::instance($otheruser->id));
// Test data request creation.
$this->setUser($requester);
$datarequest = api::create_data_request($otheruser->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
$result = external::cancel_data_request($datarequest->get('id'));
$return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
$this->assertTrue($return->result);
$this->assertEmpty($return->warnings);
}
/**
* Test cancellation of a request where you are the requester of another user's data.
*/
public function test_cancel_data_request_requester_lost_permissions(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$otheruser = $generator->create_user();
$comment = 'sample comment';
// Assign requester as otheruser'sparent.
$systemcontext = \context_system::instance();
$parentrole = $generator->create_role();
assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext);
role_assign($parentrole, $requester->id, \context_user::instance($otheruser->id));
$this->setUser($requester);
$datarequest = api::create_data_request($otheruser->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Unassign the role.
role_unassign($parentrole, $requester->id, \context_user::instance($otheruser->id)->id);
// This user can no longer make the request.
$this->expectException(\required_capability_exception::class);
$result = external::cancel_data_request($datarequest->get('id'));
}
/**
* Test cancellation of a request where you are the requester of another user's data.
*/
public function test_cancel_data_request_other_user_as_child(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$otheruser = $generator->create_user();
$comment = 'sample comment';
// Assign requester as otheruser'sparent.
$systemcontext = \context_system::instance();
$parentrole = $generator->create_role();
assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext);
role_assign($parentrole, $requester->id, \context_user::instance($otheruser->id));
// Test data request creation.
$this->setUser($otheruser);
$datarequest = api::create_data_request($otheruser->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
$result = external::cancel_data_request($datarequest->get('id'));
$return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
$this->assertTrue($return->result);
$this->assertEmpty($return->warnings);
}
/**
* Test for external::cancel_data_request()
*/
public function test_cancel_data_request(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
// Test data request creation.
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Test cancellation.
$this->setUser($requester);
$result = external::cancel_data_request($datarequest->get('id'));
$return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
$this->assertTrue($return->result);
$this->assertEmpty($return->warnings);
}
/**
* Test contact DPO.
*/
public function test_contact_dpo(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$user = $generator->create_user();
$this->setUser($user);
$message = 'Hello world!';
$result = external::contact_dpo($message);
$return = (object) external_api::clean_returnvalue(external::contact_dpo_returns(), $result);
$this->assertTrue($return->result);
$this->assertEmpty($return->warnings);
}
/**
* Test contact DPO with message containing invalid input.
*/
public function test_contact_dpo_with_nasty_input(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$user = $generator->create_user();
$this->setUser($user);
$this->expectException('invalid_parameter_exception');
external::contact_dpo('de<>\\..scription');
}
/**
* Test for external::deny_data_request() with the user not logged in.
*/
public function test_deny_data_request_not_logged_in(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
// Test data request creation.
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Log out.
$this->setUser();
$this->expectException(\require_login_exception::class);
external::deny_data_request($datarequest->get('id'));
}
/**
* Test for external::deny_data_request() with the user not having a DPO role.
*/
public function test_deny_data_request_not_dpo(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Login as the requester.
$this->setUser($requester);
$this->expectException(\required_capability_exception::class);
external::deny_data_request($datarequest->get('id'));
}
/**
* Test for external::deny_data_request() for request that's not ready for approval
*/
public function test_deny_data_request_not_waiting_for_approval(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
$datarequest->set('status', api::DATAREQUEST_STATUS_CANCELLED)->save();
// Admin as DPO. (The default when no one's assigned as a DPO in the site).
$this->setAdminUser();
$this->expectException(\moodle_exception::class);
external::deny_data_request($datarequest->get('id'));
}
/**
* Test for external::deny_data_request()
*/
public function test_deny_data_request(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Admin as DPO. (The default when no one's assigned as a DPO in the site).
$this->setAdminUser();
api::update_request_status($datarequest->get('id'), api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
$result = external::approve_data_request($datarequest->get('id'));
$return = (object) external_api::clean_returnvalue(external::deny_data_request_returns(), $result);
$this->assertTrue($return->result);
$this->assertEmpty($return->warnings);
}
/**
* Test for external::deny_data_request() for a non-existent request ID.
*/
public function test_deny_data_request_non_existent(): void {
$this->resetAfterTest();
// Admin as DPO. (The default when no one's assigned as a DPO in the site).
$this->setAdminUser();
$result = external::deny_data_request(1);
$return = (object) external_api::clean_returnvalue(external::deny_data_request_returns(), $result);
$this->assertFalse($return->result);
$this->assertCount(1, $return->warnings);
$warning = reset($return->warnings);
$this->assertEquals('errorrequestnotfound', $warning['warningcode']);
}
/**
* Test for external::get_data_request() with the user not logged in.
*/
public function test_get_data_request_not_logged_in(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
$this->setUser();
$this->expectException(\require_login_exception::class);
external::get_data_request($datarequest->get('id'));
}
/**
* Test for external::get_data_request() with the user not having a DPO role.
*/
public function test_get_data_request_not_dpo(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$otheruser = $generator->create_user();
$comment = 'sample comment';
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Login as the otheruser.
$this->setUser($otheruser);
$this->expectException(\required_capability_exception::class);
external::get_data_request($datarequest->get('id'));
}
/**
* Test for external::get_data_request()
*/
public function test_get_data_request(): void {
$this->resetAfterTest();
$generator = new \testing_data_generator();
$requester = $generator->create_user();
$comment = 'sample comment';
$this->setUser($requester);
$datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
// Admin as DPO. (The default when no one's assigned as a DPO in the site).
$this->setAdminUser();
$result = external::get_data_request($datarequest->get('id'));
$return = (object) external_api::clean_returnvalue(external::get_data_request_returns(), $result);
$this->assertEquals(api::DATAREQUEST_TYPE_EXPORT, $return->result['type']);
$this->assertEquals('sample comment', $return->result['comments']);
$this->assertEquals($requester->id, $return->result['userid']);
$this->assertEquals($requester->id, $return->result['requestedby']);
$this->assertEmpty($return->warnings);
}
/**
* Test for external::get_data_request() for a non-existent request ID.
*/
public function test_get_data_request_non_existent(): void {
$this->resetAfterTest();
// Admin as DPO. (The default when no one's assigned as a DPO in the site).
$this->setAdminUser();
$this->expectException(\dml_missing_record_exception::class);
external::get_data_request(1);
}
/**
* Test for \tool_dataprivacy\external::set_context_defaults()
* when called by a user that doesn't have the manage registry capability.
*/
public function test_set_context_defaults_no_capability(): void {
$this->resetAfterTest();
$generator = $this->getDataGenerator();
$user = $generator->create_user();
$this->setUser($user);
$this->expectException(\required_capability_exception::class);
external::set_context_defaults(CONTEXT_COURSECAT, context_instance::INHERIT, context_instance::INHERIT, '', false);
}
/**
* Test for \tool_dataprivacy\external::set_context_defaults().
*
* We're just checking the module context level here to test the WS function.
* More testing is done in \tool_dataprivacy_api_testcase::test_set_context_defaults().
*
* @dataProvider get_options_provider
* @param bool $modulelevel Whether defaults are to be applied on the module context level or for an activity only.
* @param bool $override Whether to override instances.
*/
public function test_set_context_defaults($modulelevel, $override): void {
$this->resetAfterTest();
$this->setAdminUser();
$generator = $this->getDataGenerator();
// Generate course cat, course, block, assignment, forum instances.
$coursecat = $generator->create_category();
$course = $generator->create_course(['category' => $coursecat->id]);
$assign = $generator->create_module('assign', ['course' => $course->id]);
list($course, $assigncm) = get_course_and_cm_from_instance($assign->id, 'assign');
$assigncontext = \context_module::instance($assigncm->id);
// Generate purpose and category.
$category1 = api::create_category((object)['name' => 'Test category 1']);
$category2 = api::create_category((object)['name' => 'Test category 2']);
$purpose1 = api::create_purpose((object)[
'name' => 'Test purpose 1', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a'
]);
$purpose2 = api::create_purpose((object)[
'name' => 'Test purpose 2', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a'
]);
// Set a custom purpose and ID for this assignment instance.
$assignctxinstance = api::set_context_instance((object) [
'contextid' => $assigncontext->id,
'purposeid' => $purpose1->get('id'),
'categoryid' => $category1->get('id'),
]);
$modulename = $modulelevel ? 'assign' : '';
$categoryid = $category2->get('id');
$purposeid = $purpose2->get('id');
$result = external::set_context_defaults(CONTEXT_MODULE, $categoryid, $purposeid, $modulename, $override);
// Extract the result.
$return = external_api::clean_returnvalue(external::set_context_defaults_returns(), $result);
$this->assertTrue($return['result']);
// Check the assignment context instance.
$instanceexists = context_instance::record_exists($assignctxinstance->get('id'));
if ($override) {
// The custom assign instance should have been deleted.
$this->assertFalse($instanceexists);
} else {
// The custom assign instance should still exist.
$this->assertTrue($instanceexists);
}
// Check the saved defaults.
list($savedpurpose, $savedcategory) = \tool_dataprivacy\data_registry::get_defaults(CONTEXT_MODULE, $modulename);
$this->assertEquals($categoryid, $savedcategory);
$this->assertEquals($purposeid, $savedpurpose);
}
/**
* Test for \tool_dataprivacy\external::get_category_options()
* when called by a user that doesn't have the manage registry capability.
*/
public function test_get_category_options_no_capability(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(\required_capability_exception::class);
external::get_category_options(true, true);
}
/**
* Data provider for \tool_dataprivacy_external_testcase::test_XX_options().
*/
public function get_options_provider() {
return [
[false, false],
[false, true],
[true, false],
[true, true],
];
}
/**
* Test for \tool_dataprivacy\external::get_category_options().
*
* @dataProvider get_options_provider
* @param bool $includeinherit Whether "Inherit" would be included to the options.
* @param bool $includenotset Whether "Not set" would be included to the options.
*/
public function test_get_category_options($includeinherit, $includenotset): void {
$this->resetAfterTest();
$this->setAdminUser();
// Prepare our expected options.
$expectedoptions = [];
if ($includeinherit) {
$expectedoptions[] = [
'id' => context_instance::INHERIT,
'name' => get_string('inherit', 'tool_dataprivacy'),
];
}
if ($includenotset) {
$expectedoptions[] = [
'id' => context_instance::NOTSET,
'name' => get_string('notset', 'tool_dataprivacy'),
];
}
for ($i = 1; $i <= 3; $i++) {
$category = api::create_category((object)['name' => 'Category ' . $i]);
$expectedoptions[] = [
'id' => $category->get('id'),
'name' => $category->get('name'),
];
}
// Call the WS function.
$result = external::get_category_options($includeinherit, $includenotset);
// Extract the options.
$return = (object) external_api::clean_returnvalue(external::get_category_options_returns(), $result);
$options = $return->options;
// Make sure everything checks out.
$this->assertCount(count($expectedoptions), $options);
foreach ($options as $option) {
$this->assertContains($option, $expectedoptions);
}
}
/**
* Test for \tool_dataprivacy\external::get_purpose_options()
* when called by a user that doesn't have the manage registry capability.
*/
public function test_get_purpose_options_no_capability(): void {
$this->resetAfterTest();
$generator = $this->getDataGenerator();
$user = $generator->create_user();
$this->setUser($user);
$this->expectException(\required_capability_exception::class);
external::get_category_options(true, true);
}
/**
* Test for \tool_dataprivacy\external::get_purpose_options().
*
* @dataProvider get_options_provider
* @param bool $includeinherit Whether "Inherit" would be included to the options.
* @param bool $includenotset Whether "Not set" would be included to the options.
*/
public function test_get_purpose_options($includeinherit, $includenotset): void {
$this->resetAfterTest();
$this->setAdminUser();
// Prepare our expected options.
$expectedoptions = [];
if ($includeinherit) {
$expectedoptions[] = [
'id' => context_instance::INHERIT,
'name' => get_string('inherit', 'tool_dataprivacy'),
];
}
if ($includenotset) {
$expectedoptions[] = [
'id' => context_instance::NOTSET,
'name' => get_string('notset', 'tool_dataprivacy'),
];
}
for ($i = 1; $i <= 3; $i++) {
$purpose = api::create_purpose((object)[
'name' => 'Purpose ' . $i, 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a'
]);
$expectedoptions[] = [
'id' => $purpose->get('id'),
'name' => $purpose->get('name'),
];
}
// Call the WS function.
$result = external::get_purpose_options($includeinherit, $includenotset);
// Extract the options.
$return = (object) external_api::clean_returnvalue(external::get_purpose_options_returns(), $result);
$options = $return->options;
// Make sure everything checks out.
$this->assertCount(count($expectedoptions), $options);
foreach ($options as $option) {
$this->assertContains($option, $expectedoptions);
}
}
/**
* Data provider for \tool_dataprivacy_external_testcase::get_activity_options().
*/
public function get_activity_options_provider() {
return [
[false, false, true],
[false, true, true],
[true, false, true],
[true, true, true],
[false, false, false],
[false, true, false],
[true, false, false],
[true, true, false],
];
}
/**
* Test for \tool_dataprivacy\external::get_activity_options().
*
* @dataProvider get_activity_options_provider
* @param bool $inheritcategory Whether the category would be set to "Inherit".
* @param bool $inheritpurpose Whether the purpose would be set to "Inherit".
* @param bool $nodefaults Whether to fetch only activities that don't have defaults.
*/
public function test_get_activity_options($inheritcategory, $inheritpurpose, $nodefaults): void {
$this->resetAfterTest();
$this->setAdminUser();
$category = api::create_category((object)['name' => 'Test category']);
$purpose = api::create_purpose((object)[
'name' => 'Test purpose ', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a'
]);
$categoryid = $category->get('id');
$purposeid = $purpose->get('id');
if ($inheritcategory) {
$categoryid = context_instance::INHERIT;
}
if ($inheritpurpose) {
$purposeid = context_instance::INHERIT;
}
// Set the context default for the assignment module.
api::set_context_defaults(CONTEXT_MODULE, $categoryid, $purposeid, 'assign');
// Call the WS function.
$result = external::get_activity_options($nodefaults);
// Extract the options.
$return = (object) external_api::clean_returnvalue(external::get_activity_options_returns(), $result);
$options = $return->options;
// Make sure the options list is not empty.
$this->assertNotEmpty($options);
$pluginwithdefaults = [
'name' => 'assign',
'displayname' => get_string('pluginname', 'assign')
];
// If we don't want plugins with defaults to be listed or if both of the category and purpose are set to inherit,
// the assign module should be listed.
if (!$nodefaults || ($inheritcategory && $inheritpurpose)) {
$this->assertContains($pluginwithdefaults, $options);
} else {
$this->assertNotContains($pluginwithdefaults, $options);
}
}
/**
* Test for external::bulk_approve_data_requests().
*/
public function test_bulk_approve_data_requests(): void {
$this->resetAfterTest();
// Create delete data requests.
$requester1 = $this->getDataGenerator()->create_user();
$this->setUser($requester1->id);
$datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid1 = $datarequest1->get('id');
$requester2 = $this->getDataGenerator()->create_user();
$this->setUser($requester2->id);
$datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid2 = $datarequest2->get('id');
// Approve the requests.
$this->setAdminUser();
api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
$result = external::bulk_approve_data_requests([$requestid1, $requestid2]);
$return = (object) external_api::clean_returnvalue(external::bulk_approve_data_requests_returns(), $result);
$this->assertTrue($return->result);
$this->assertEmpty($return->warnings);
}
/**
* Test for external::bulk_approve_data_requests() for a non-existent request ID.
*/
public function test_bulk_approve_data_requests_non_existent(): void {
$this->resetAfterTest();
$this->setAdminUser();
$result = external::bulk_approve_data_requests([42]);
$return = (object) external_api::clean_returnvalue(external::bulk_approve_data_requests_returns(), $result);
$this->assertFalse($return->result);
$this->assertCount(1, $return->warnings);
$warning = reset($return->warnings);
$this->assertEquals('errorrequestnotfound', $warning['warningcode']);
$this->assertEquals(42, $warning['item']);
}
/**
* Test for external::bulk_deny_data_requests() for a user without permission to deny requests.
*/
public function test_bulk_approve_data_requests_no_permission(): void {
$this->resetAfterTest();
// Create delete data requests.
$requester1 = $this->getDataGenerator()->create_user();
$this->setUser($requester1->id);
$datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid1 = $datarequest1->get('id');
$requester2 = $this->getDataGenerator()->create_user();
$this->setUser($requester2->id);
$datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid2 = $datarequest2->get('id');
$this->setAdminUser();
api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
// Approve the requests.
$uut = $this->getDataGenerator()->create_user();
$this->setUser($uut);
$this->expectException(\required_capability_exception::class);
$result = external::bulk_approve_data_requests([$requestid1, $requestid2]);
}
/**
* Test for external::bulk_deny_data_requests() for a user without permission to deny requests.
*/
public function test_bulk_approve_data_requests_own_request(): void {
$this->resetAfterTest();
// Create delete data requests.
$requester1 = $this->getDataGenerator()->create_user();
$this->setUser($requester1->id);
$datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid1 = $datarequest1->get('id');
$requester2 = $this->getDataGenerator()->create_user();
$this->setUser($requester2->id);
$datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid2 = $datarequest2->get('id');
$this->setAdminUser();
api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
// Deny the requests.
$this->setUser($requester1);
$this->expectException(\required_capability_exception::class);
$result = external::bulk_approve_data_requests([$requestid1]);
}
/**
* Test for external::bulk_deny_data_requests().
*/
public function test_bulk_deny_data_requests(): void {
$this->resetAfterTest();
// Create delete data requests.
$requester1 = $this->getDataGenerator()->create_user();
$this->setUser($requester1->id);
$datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid1 = $datarequest1->get('id');
$requester2 = $this->getDataGenerator()->create_user();
$this->setUser($requester2->id);
$datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid2 = $datarequest2->get('id');
// Deny the requests.
$this->setAdminUser();
api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
$result = external::bulk_deny_data_requests([$requestid1, $requestid2]);
$return = (object) external_api::clean_returnvalue(external::bulk_approve_data_requests_returns(), $result);
$this->assertTrue($return->result);
$this->assertEmpty($return->warnings);
}
/**
* Test for external::bulk_deny_data_requests() for a non-existent request ID.
*/
public function test_bulk_deny_data_requests_non_existent(): void {
$this->resetAfterTest();
$this->setAdminUser();
$result = external::bulk_deny_data_requests([42]);
$return = (object) external_api::clean_returnvalue(external::bulk_approve_data_requests_returns(), $result);
$this->assertFalse($return->result);
$this->assertCount(1, $return->warnings);
$warning = reset($return->warnings);
$this->assertEquals('errorrequestnotfound', $warning['warningcode']);
$this->assertEquals(42, $warning['item']);
}
/**
* Test for external::bulk_deny_data_requests() for a user without permission to deny requests.
*/
public function test_bulk_deny_data_requests_no_permission(): void {
$this->resetAfterTest();
// Create delete data requests.
$requester1 = $this->getDataGenerator()->create_user();
$this->setUser($requester1->id);
$datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid1 = $datarequest1->get('id');
$requester2 = $this->getDataGenerator()->create_user();
$this->setUser($requester2->id);
$datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid2 = $datarequest2->get('id');
$this->setAdminUser();
api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
// Deny the requests.
$uut = $this->getDataGenerator()->create_user();
$this->setUser($uut);
$this->expectException(\required_capability_exception::class);
$result = external::bulk_deny_data_requests([$requestid1, $requestid2]);
}
/**
* Test for external::bulk_deny_data_requests() for a user cannot approve their own request.
*/
public function test_bulk_deny_data_requests_own_request(): void {
$this->resetAfterTest();
// Create delete data requests.
$requester1 = $this->getDataGenerator()->create_user();
$this->setUser($requester1->id);
$datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid1 = $datarequest1->get('id');
$requester2 = $this->getDataGenerator()->create_user();
$this->setUser($requester2->id);
$datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
$requestid2 = $datarequest2->get('id');
$this->setAdminUser();
api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
// Deny the requests.
$this->setUser($requester1);
$this->expectException(\required_capability_exception::class);
$result = external::bulk_deny_data_requests([$requestid1]);
}
/**
* Test for external::get_users(), case search using non-identity field without
* facing any permission problem.
*
* @throws coding_exception
* @throws dml_exception
* @throws invalid_parameter_exception
* @throws required_capability_exception
* @throws restricted_context_exception
*/
public function test_get_users_using_using_non_identity(): void {
$this->resetAfterTest();
$context = \context_system::instance();
$requester = $this->getDataGenerator()->create_user();
$role = $this->getDataGenerator()->create_role();
role_assign($role, $requester->id, $context);
assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
$this->setUser($requester);
$this->getDataGenerator()->create_user([
'firstname' => 'First Student'
]);
$student2 = $this->getDataGenerator()->create_user([
'firstname' => 'Second Student'
]);
$results = external::get_users('Second');
$this->assertCount(1, $results);
$this->assertEquals((object)[
'id' => $student2->id,
'fullname' => fullname($student2),
'extrafields' => []
], $results[$student2->id]);
}
/**
* Test for external::get_users(), case search using identity field but
* don't have "moodle/site:viewuseridentity" permission.
*
* @throws coding_exception
* @throws dml_exception
* @throws invalid_parameter_exception
* @throws required_capability_exception
* @throws restricted_context_exception
*/
public function test_get_users_using_identity_without_permission(): void {
global $CFG;
$this->resetAfterTest();
$CFG->showuseridentity = 'institution';
// Create requester user and assign correct capability.
$context = \context_system::instance();
$requester = $this->getDataGenerator()->create_user();
$role = $this->getDataGenerator()->create_role();
role_assign($role, $requester->id, $context);
assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
$this->setUser($requester);
$this->getDataGenerator()->create_user([
'institution' => 'University1'
]);
$results = external::get_users('University1');
$this->assertEmpty($results);
}
/**
* Test for external::get_users(), case search using disabled identity field
* even they have "moodle/site:viewuseridentity" permission.
*
* @throws coding_exception
* @throws dml_exception
* @throws invalid_parameter_exception
* @throws required_capability_exception
* @throws restricted_context_exception
*/
public function test_get_users_using_field_not_in_identity(): void {
$this->resetAfterTest();
$context = \context_system::instance();
$requester = $this->getDataGenerator()->create_user();
$role = $this->getDataGenerator()->create_role();
role_assign($role, $requester->id, $context);
assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
assign_capability('moodle/site:viewuseridentity', CAP_ALLOW, $role, $context);
$this->setUser($requester);
$this->getDataGenerator()->create_user([
'institution' => 'University1'
]);
$results = external::get_users('University1');
$this->assertEmpty($results);
}
/**
* Test for external::get_users(), case search using enabled identity field
* with "moodle/site:viewuseridentity" permission.
*
* @throws coding_exception
* @throws dml_exception
* @throws invalid_parameter_exception
* @throws required_capability_exception
* @throws restricted_context_exception
*/
public function test_get_users(): void {
global $CFG;
$this->resetAfterTest();
$CFG->showuseridentity = 'institution';
$context = \context_system::instance();
$requester = $this->getDataGenerator()->create_user();
$role = $this->getDataGenerator()->create_role();
role_assign($role, $requester->id, $context);
assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
assign_capability('moodle/site:viewuseridentity', CAP_ALLOW, $role, $context);
$this->setUser($requester);
$student1 = $this->getDataGenerator()->create_user([
'institution' => 'University1'
]);
$this->getDataGenerator()->create_user([
'institution' => 'University2'
]);
$results = external::get_users('University1');
$this->assertCount(1, $results);
$this->assertEquals((object)[
'id' => $student1->id,
'fullname' => fullname($student1),
'extrafields' => [
0 => (object)[
'name' => 'institution',
'value' => 'University1'
]
]
], $results[$student1->id]);
}
/**
* Test for external::get_access_information().
*/
public function test_get_access_information(): void {
$this->resetAfterTest();
$this->setAdminUser();
$result = get_access_information::execute();
$result = external_api::clean_returnvalue(get_access_information::execute_returns(), $result);
$this->assertFalse($result['cancontactdpo']); // Disabled by default.
// Enable contact DPO.
set_config('contactdataprotectionofficer', 1, 'tool_dataprivacy');
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$result = get_access_information::execute();
$result = external_api::clean_returnvalue(get_access_information::execute_returns(), $result);
$this->assertTrue($result['cancontactdpo']);
$this->assertTrue($result['cancreatedatadownloadrequest']);
$this->assertTrue($result['cancreatedatadeletionrequest']);
$this->assertFalse($result['canmanagedatarequests']);
$this->assertFalse($result['hasongoingdatadownloadrequest']);
$this->assertFalse($result['hasongoingdatadeletionrequest']);
}
/**
* Test for external::create_data_request()
*/
public function test_create_data_request(): void {
$this->resetAfterTest();
$systemcontext = \context_system::instance();
$user = $this->getDataGenerator()->create_user();
$requester = $this->getDataGenerator()->create_user();
$role = $this->getDataGenerator()->create_role();
assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $role, $systemcontext);
role_assign($role, $requester->id, \context_user::instance($user->id));
// Enable contact DPO.
set_config('contactdataprotectionofficer', 1, 'tool_dataprivacy');
// Create a data request for myself.
$this->setUser($user);
$comment = 'Example comment';
$result = create_data_request::execute(api::DATAREQUEST_TYPE_DELETE, $comment);
$result = external_api::clean_returnvalue(create_data_request::execute_returns(), $result);
$this->assertEmpty($result['warnings']);
$requests = api::get_data_requests($user->id);
$this->assertCount(1, $requests);
$request = reset($requests);
$this->assertEquals($result['datarequestid'], $request->get('id'));
$this->assertEquals($user->id, $request->get('userid'));
$this->assertEquals(api::DATAREQUEST_TYPE_DELETE, $request->get('type'));
$this->assertEquals($comment, $request->get('comments'));
// Create on behalf of other user a different type of request.
$this->setUser($requester);
$comment = 'Example comment';
$result = create_data_request::execute(api::DATAREQUEST_TYPE_EXPORT, $comment, $user->id);
$result = external_api::clean_returnvalue(create_data_request::execute_returns(), $result);
$this->assertEmpty($result['warnings']);
$requests = api::get_data_requests($user->id);
$this->assertCount(2, $requests);
foreach ($requests as $request) {
if ($request->get('type') == api::DATAREQUEST_TYPE_EXPORT) {
$this->assertEquals($result['datarequestid'], $request->get('id'));
$this->assertEquals($user->id, $request->get('userid'));
$this->assertEquals($comment, $request->get('comments'));
} else {
$this->assertEquals($user->id, $request->get('userid'));
$this->assertEquals(api::DATAREQUEST_TYPE_DELETE, $request->get('type'));
$this->assertEquals('Example comment', $request->get('comments'));
}
}
}
/**
* Test for external::create_data_request() when no dpo available.
*/
public function test_create_data_request_no_dpo(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(\moodle_exception::class);
$this->expectExceptionMessage(get_string('contactdpoviaprivacypolicy', 'tool_dataprivacy'));
create_data_request::execute(api::DATAREQUEST_TYPE_DELETE, 'Example comment');
}
/**
* Test for external::create_data_request() with missing permission.
*/
public function test_create_data_request_no_permission(): void {
$this->resetAfterTest();
// Enable contact DPO.
set_config('contactdataprotectionofficer', 1, 'tool_dataprivacy');
$user = $this->getDataGenerator()->create_user();
$anotheruser = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(\required_capability_exception::class);
create_data_request::execute(api::DATAREQUEST_TYPE_DELETE, 'Example comment', $anotheruser->id);
}
/**
* Test for external::create_data_request() with invalid request type.
*/
public function test_create_data_request_invalid_type(): void {
$this->resetAfterTest();
// Enable contact DPO.
set_config('contactdataprotectionofficer', 1, 'tool_dataprivacy');
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(\moodle_exception::class);
$this->expectExceptionMessage(get_string('errorinvalidrequesttype', 'tool_dataprivacy'));
create_data_request::execute(125, 'Example comment');
}
/**
* Test for external::get_data_requests().
*/
public function test_get_data_requests(): void {
global $DB;
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$anotheruser = $this->getDataGenerator()->create_user();
$this->setUser($user);
// Empty results.
$result = get_data_requests::execute($user->id);
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertEmpty($result['warnings']);
$this->assertEmpty($result['requests']);
// Create data requests.
$comment = 'Example comment';
$request1 = api::create_data_request($user->id, api::DATAREQUEST_TYPE_DELETE, $comment);
$request2 = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
$request3 = api::create_data_request($anotheruser->id, api::DATAREQUEST_TYPE_EXPORT, $comment,
data_request::DATAREQUEST_CREATION_AUTO);
// Get data requests.
$result = get_data_requests::execute($user->id);
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertEmpty($result['warnings']);
$this->assertCount(2, $result['requests']);
foreach ($result['requests'] as $request) {
if ($request['id'] == $request1->get('id')) {
$this->assertEquals($user->id, $request['userid']);
$this->assertEquals(api::DATAREQUEST_TYPE_DELETE, $request['type']);
$this->assertEquals($comment, $request['comments']);
} else {
$this->assertEquals($user->id, $request['userid']);
$this->assertEquals(api::DATAREQUEST_TYPE_EXPORT, $request['type']);
$this->assertEquals($comment, $request['comments']);
}
$this->assertArrayNotHasKey('downloadlink', $request); // Download link only present for download ready requests.
}
// Filter by type.
$result = get_data_requests::execute($user->id, [], [api::DATAREQUEST_TYPE_DELETE]);
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertCount(1, $result['requests']);
$this->assertEquals($request1->get('id'), $result['requests'][0]['id']);
// Admin get all.
$this->setAdminUser();
$result = get_data_requests::execute();
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertCount(3, $result['requests']);
// Test limit.
$result = get_data_requests::execute(0, [], [], [], '', 2, 1);
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertCount(1, $result['requests']);
// Test sort.
$result = get_data_requests::execute(0, [], [], [], 'id DESC');
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertEquals($request1->get('id'), $result['requests'][2]['id']);
$this->assertEquals($request2->get('id'), $result['requests'][1]['id']);
$this->assertEquals($request3->get('id'), $result['requests'][0]['id']);
// Test filter by status.
api::update_request_status($request3->get('id'), api::DATAREQUEST_STATUS_DOWNLOAD_READY);
$result = get_data_requests::execute(0, [api::DATAREQUEST_STATUS_DOWNLOAD_READY]);
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertCount(1, $result['requests']);
$this->assertEquals($request3->get('id'), $result['requests'][0]['id']);
// Check download link because the download is now ready.
$usercontext = \context_user::instance($anotheruser->id, IGNORE_MISSING);
$downloadlink = api::get_download_link($usercontext, $result['requests'][0]['id'])->url;
$this->assertEquals($downloadlink->out(false), $result['requests'][0]['downloadlink']);
// Test filter by creation method.
$result = get_data_requests::execute(0, [], [], [data_request::DATAREQUEST_CREATION_AUTO]);
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertCount(1, $result['requests']);
$this->assertEquals($request3->get('id'), $result['requests'][0]['id']);
// Get data requests for another user without required permissions.
$userrole = $DB->get_field('role', 'id', ['shortname' => 'user'], MUST_EXIST);
assign_capability('tool/dataprivacy:downloadownrequest', CAP_PROHIBIT, $userrole, \context_user::instance($anotheruser->id));
$this->setUser($anotheruser);
// Get my data request ready for download but without permissons for download it.
$result = get_data_requests::execute($anotheruser->id, [api::DATAREQUEST_STATUS_DOWNLOAD_READY]);
$result = external_api::clean_returnvalue(get_data_requests::execute_returns(), $result);
$this->assertArrayNotHasKey('downloadlink', $result['requests'][0]); // Download link is not present.
// And now try to see a different user requests.
$this->expectException(\moodle_exception::class);
$dponamestring = implode (', ', api::get_dpo_role_names());
$this->expectExceptionMessage(get_string('privacyofficeronly', 'tool_dataprivacy', $dponamestring));
$result = get_data_requests::execute($user->id);
}
/**
* Test for external::get_data_requests() invalid user id.
*/
public function test_get_data_requests_invalid_userid(): void {
$this->resetAfterTest();
$this->setAdminUser();
$this->expectException(\dml_exception::class);
get_data_requests::execute(-1);
}
}