MDL-63664 tool_policy: Add support for removal of context users
authorMichael Hawkins <michaelh@moodle.com>
Tue, 16 Oct 2018 05:48:49 +0000 (13:48 +0800)
committerMichael Hawkins <michaelh@moodle.com>
Tue, 23 Oct 2018 02:02:58 +0000 (10:02 +0800)
This issue is a part of the MDL-62560 Epic.

admin/tool/policy/classes/privacy/provider.php
admin/tool/policy/tests/privacy_provider_test.php

index 67e4e51..cf9116d 100644 (file)
@@ -26,8 +26,10 @@ namespace tool_policy\privacy;
 
 use core_privacy\local\metadata\collection;
 use core_privacy\local\request\approved_contextlist;
+use core_privacy\local\request\approved_userlist;
 use core_privacy\local\request\contextlist;
 use core_privacy\local\request\moodle_content_writer;
+use core_privacy\local\request\userlist;
 use core_privacy\local\request\transform;
 use core_privacy\local\request\writer;
 
@@ -43,6 +45,9 @@ class provider implements
         // This tool stores user data.
         \core_privacy\local\metadata\provider,
 
+        // This plugin is capable of determining which users have data within it.
+        \core_privacy\local\request\core_userlist_provider,
+
         // This tool may provide access to and deletion of user data.
         \core_privacy\local\request\plugin\provider {
 
@@ -132,6 +137,33 @@ class provider implements
         return $contextlist;
     }
 
+    /**
+     * Get the list of users who have data within a context.
+     *
+     * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
+     */
+    public static function get_users_in_context(userlist $userlist) {
+        $context = $userlist->get_context();
+
+        // Users that have modified any policies, if fetching for system context.
+        if (is_a($context, \context_system::class)) {
+            $sql = "SELECT v.usermodified AS userid
+                      FROM {tool_policy_versions} v";
+            $userlist->add_from_sql('userid', $sql, []);
+        }
+
+        // Users that have accepted any policies, if fetching for user context.
+        if (is_a($context, \context_user::class)) {
+            $sql = "SELECT a.userid, a.usermodified
+                      FROM {tool_policy_acceptances} a
+                     WHERE a.userid = :instanceid";
+            $params = ['instanceid' => $context->instanceid];
+
+            $userlist->add_from_sql('userid', $sql, $params);
+            $userlist->add_from_sql('usermodified', $sql, $params);
+        }
+    }
+
     /**
      * Export personal data for the given approved_contextlist. User and context information is contained within the contextlist.
      *
@@ -172,6 +204,17 @@ class provider implements
     public static function delete_data_for_user(approved_contextlist $contextlist) {
     }
 
+    /**
+     * Delete multiple users within a single context.
+     *
+     * We never delete user agreements to the policies because they are part of privacy data.
+     * We never delete policy versions because they are part of privacy data.
+     *
+     * @param   approved_userlist       $userlist The approved context and user information to delete information for.
+     */
+    public static function delete_data_for_users(approved_userlist $userlist) {
+    }
+
     /**
      * Export all policy agreements relating to the specified user context.
      *
index 78556c9..3e2b146 100644 (file)
@@ -45,6 +45,9 @@ class tool_policy_privacy_provider_testcase extends \core_privacy\tests\provider
     /** @var stdClass The manager user object. */
     protected $manager;
 
+/** @var context_system The system context instance. */
+    protected $syscontext;
+
     /**
      * Setup function. Will create a user.
      */
@@ -56,11 +59,11 @@ class tool_policy_privacy_provider_testcase extends \core_privacy\tests\provider
 
         // Create manager user.
         $this->manager = $generator->create_user();
-        $syscontext = context_system::instance();
+        $this->syscontext = context_system::instance();
         $rolemanagerid = create_role('Policy manager', 'policymanager', 'Can manage policy documents');
-        assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $syscontext->id);
-        assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $syscontext->id);
-        role_assign($rolemanagerid, $this->manager->id, $syscontext->id);
+        assign_capability('tool/policy:managedocs', CAP_ALLOW, $rolemanagerid, $this->syscontext->id);
+        assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $rolemanagerid, $this->syscontext->id);
+        role_assign($rolemanagerid, $this->manager->id, $this->syscontext->id);
         accesslib_clear_all_caches_for_unit_testing();
     }
 
@@ -99,6 +102,88 @@ class tool_policy_privacy_provider_testcase extends \core_privacy\tests\provider
         $this->assertEquals(1, $contextlist->count());
     }
 
+    /**
+     * Test getting the user IDs within the context related to this plugin.
+     */
+    public function test_get_users_in_context() {
+        global $CFG;
+        $component = 'tool_policy';
+
+        // System context should have nothing before a policy is added.
+        $userlist = new \core_privacy\local\request\userlist($this->syscontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertEmpty($userlist);
+
+        // Create parent and child users.
+        $generator = $this->getDataGenerator();
+        $parentuser = $generator->create_user();
+        $childuser = $generator->create_user();
+
+        // Fetch relevant contexts.
+        $managercontext = \context_user::instance($this->manager->id);
+        $usercontext = $managercontext = \context_user::instance($this->user->id);
+        $parentcontext = $managercontext = \context_user::instance($parentuser->id);
+        $childcontext = $managercontext = \context_user::instance($childuser->id);
+
+        // Assign parent to accept on behalf of the child.
+        $roleparentid = create_role('Parent', 'parent', 'Can accept policies on behalf of their child');
+        assign_capability('tool/policy:acceptbehalf', CAP_ALLOW, $roleparentid, $this->syscontext->id);
+        role_assign($roleparentid, $parentuser->id, $childcontext->id);
+
+        // Create a policy.
+        $this->setUser($this->manager);
+        $CFG->sitepolicyhandler = 'tool_policy';
+        $policy = $this->add_policy();
+        api::make_current($policy->get('id'));
+
+        // Manager should exist in system context now they have created a policy.
+        $userlist = new \core_privacy\local\request\userlist($this->syscontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertCount(1, $userlist);
+        $this->assertEquals([$this->manager->id], $userlist->get_userids());
+
+        // User contexts should be empty before policy acceptances.
+        $userlist = new \core_privacy\local\request\userlist($usercontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertEmpty($userlist);
+
+        $userlist = new \core_privacy\local\request\userlist($parentcontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertEmpty($userlist);
+
+        $userlist = new \core_privacy\local\request\userlist($childcontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertEmpty($userlist);
+
+        // User accepts policy, parent accepts on behalf of child only.
+        $this->setUser($this->user);
+        api::accept_policies([$policy->get('id')]);
+
+        $this->setUser($parentuser);
+        api::accept_policies([$policy->get('id')], $childuser->id);
+
+        // Ensure user is fetched within its user context.
+        $userlist = new \core_privacy\local\request\userlist($usercontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertCount(1, $userlist);
+        $this->assertEquals([$this->user->id], $userlist->get_userids());
+
+        // Ensure parent and child are both found within child's user context.
+        $userlist = new \core_privacy\local\request\userlist($childcontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertCount(2, $userlist);
+        $expected = [$parentuser->id, $childuser->id];
+        $actual = $userlist->get_userids();
+        sort($expected);
+        sort($actual);
+        $this->assertEquals($expected, $actual);
+
+        // Parent has not accepted for itself, so should not be found within its user context.
+        $userlist = new \core_privacy\local\request\userlist($parentcontext, $component);
+        provider::get_users_in_context($userlist);
+        $this->assertCount(0, $userlist);
+    }
+
     public function test_export_agreements() {
         global $CFG;