|
|
| (6 intermediate revisions by 2 users not shown) |
| Line 1: |
Line 1: |
| {{GDPR}}
| | #redirect [[Privacy API]] |
| ==Personal data in Moodle==
| |
| | |
| From the GDPR Spec, Article 4:
| |
| | |
| ''‘personal data’ means any information relating to an identified or identifiable natural person (‘data subject’); an identifiable natural person is one who can be identified, directly or indirectly, in particular by reference to an identifier such as a name, an identification number, location data, an online identifier or to one or more factors specific to the physical, physiological, genetic, mental, economic, cultural or social identity of that natural person;''
| |
| | |
| In Moodle, we need to consider two main types of personal data; information entered by the user and information stored about the user. The key difference being that information stored about the user will have come from a source other than the user themselves. Both types of data can be used to form a profile of the individual.
| |
| | |
| The most obvious clue to finding personal data entered by the user is the presence of a userid on a database field. Any data on the record (or linked records) pertaining to that user may be deemed personal data for that user, including things like timestamps and record identification numbers. Additionally, any free text field which allows the user to enter information must also be considered to be the personal data of that user.
| |
| | |
| Data stored about the user includes things like ratings and comments made on a student submission. These may have been made by an assessor or teacher, but are considered the personal data of the student, as they are considered a reflection of the user’s competency in the subject matter and can be used to form a profile of that individual.
| |
| | |
| The sections that follow outline what you need to do as a plugin developer to ensure any personal data is advertised and can be accessed and deleted according to the GDPR requirements.
| |
| | |
| ==Background==
| |
| | |
| ===Architecture overview===
| |
| | |
| A new system for Privacy has been created within Moodle. This is broken down into several main parts and forms the ''core_privacy'' subsystem:
| |
| | |
| * Some metadata providers - a set of PHP interfaces to be implemented by components for that component to describe the kind of data that it stores, and the purpose for its storage;
| |
| * Some request providers - a set of PHP interfaces to be implemented by components to allow that component to act upon user requests such as the Right to be Forgotten, and a Subject Access Request; and
| |
| * A manager - a concrete class used to bridge components which implement the providers with tools which request their data.
| |
| | |
| All plugins will implement one metadata provider, and zero, one or two request providers.
| |
| | |
| The fetching of data is broken into two separate steps:
| |
| | |
| # Detecting in which Moodle contexts the user has any data; and
| |
| # Exporting all data from each of those contexts.
| |
| | |
| This has been broken into two steps to later allow administrators to exclude certain contexts from an export - e.g. for courses currently in progress.
| |
| | |
| A third component will later be added to facilitate the deletion of data within these contexts which will help to satisfy the Right to be Forgotten. This will also use the first step.
| |
| | |
| ===Implementing a provider===
| |
| | |
| All plugins will need to create a concrete class which implements the relevant metadata and request providers. The exact providers you need to implement will depend on what data you store, and the type of plugin. This is covered in more detail in the following sections of the document.
| |
| | |
| In order to do so:
| |
| | |
| # You must create a class called ''provider'' within the namespace ''\your_pluginname\privacy''.
| |
| # This class must be created at ''path/to/your/plugin/classes/privacy/provider.php''.
| |
| # You must have your class implement the relevant metadata and request interfaces.
| |
| | |
| ==Plugins which do not store personal data==
| |
| | |
| Many Moodle plugins do not store any personal data. This is usually the case for plugins which just add functionality, or which display the data already stored elsewhere in Moodle.
| |
| | |
| Some examples of plugin types which might fit this criteria include themes, blocks, filters, editor plugins, etc.
| |
| | |
| Plugins which cause data to be stored elsewhere in Moodle (e.g. via a subsystem call) are considered to store data.
| |
| | |
| One examples of a plugin which does not store any data would be the Calendar month block which just displays a view of the user’s calendar. It does not store any data itself.
| |
| | |
| An example of a plugin which must not use the null provider is the Comments block. The comments block is responsible for data subsequently being stored within Moodle. Although the block doesn’t store anything itself, it interacts with the comments subsystem and is the only component which knows how that data maps to a user.
| |
| | |
| ===Implementation requirements===
| |
| | |
| In order to let Moodle know that you have audited your plugin, and that you do not store any personal user data, you must implement the ''\core_privacy\local\metadata\null_provider'' interface in your plugin’s provider.
| |
| | |
| The ''null_provider'' requires you to define one function ''get_reason()'' which returns the language string identifier within your component.
| |
| | |
| ====Example====
| |
| | |
| ''block/calendar_month/classes/privacy/provider.php''
| |
| | |
| <code php>
| |
| <?php
| |
| // …
| |
| | |
| namespace block_calendar_month\privacy;
| |
| | |
| class provider implements
| |
| # This plugin does not store any personal user data.
| |
| \core_privacy\local\metadata\null_provider
| |
| {
| |
| | |
| /**
| |
| * Get the language string identifier with the component's language
| |
| * file to explain why this plugin stores no data.
| |
| *
| |
| * @return string
| |
| */
| |
| public static function get_reason() : string {
| |
| return 'privacy:null_reason';
| |
| }
| |
| }
| |
| </code>
| |
| | |
| ''block/calendar_month/lang/en/block_calendar_month.php''
| |
| | |
| <code php>
| |
| <?php
| |
| | |
| $string['privacy:null_reason'] = 'The calendar month block displays information from the Calendar, but does not effect or store any data itself. All changes are made via the Calendar.';
| |
| </code>
| |
| | |
| That’s it. Congratulations, your plugin now implements the Privacy API.
| |
| | |
| ==Plugins which store personal data==
| |
| | |
| Many Moodle plugins do store some form of personal data.
| |
| | |
| In some cases this will be stored within database tables in your plugin, and in other cases this will be in one of Moodle’s core subsystems - for example your plugin may store files, ratings, comments, or tags.
| |
| | |
| Plugins which do store data will need to:
| |
| | |
| * Describe the type of data that they store;
| |
| * Provide a way to export that data; and
| |
| * Provide a way to delete that data.
| |
| | |
| Data is described via a ''metadata'' provider, and it is both exported and deleted via an implementation of a ''request'' provider.
| |
| | |
| These are both explained in the sections below.
| |
| | |
| ===Describing the type of data you store===
| |
| | |
| In order to describe the type of data that you store, you must implement the ''\core_privacy\local\metadata\provider'' interface.
| |
| | |
| This interfaces requires that you define one function: ''get_metadata''.
| |
| | |
| There are several types of item to describe the data that you store. These are for:
| |
| | |
| * Items in the Moodle database;
| |
| * Items stored by you in a Moodle subsystem - for example files, and ratings; and
| |
| * User preferences stored site-wide within Moodle for your plugin
| |
| | |
| Note: All fields should include a description from a language string within your plugin.
| |
| | |
| ====Example====
| |
| | |
| ''mod/forum/classes/privacy/provider.php''
| |
| | |
| <code php>
| |
| <?php
| |
| // …
| |
| | |
| namespace mod_forum\privacy;
| |
| use \core_privacy\local\metadata\collection;
| |
| | |
| class provider implements
| |
| # This plugin does store personal user data.
| |
| \core_privacy\local\metadata\provider
| |
| {
| |
| public static function get_metadata(collection $collection) : collection {
| |
| return $collection;
| |
| }
| |
| }
| |
| </code>
| |
| | |
| ===Indicating that you store content in a Moodle subsystem===
| |
| | |
| Many plugins will use one of the core Moodle subsystems to store data.
| |
| | |
| As a plugin developer we do not expect you to describe those subsystems in detail, but we do need to know that you use them and to know what you use them for.
| |
| | |
| You can indicate this by calling the ''link_subsystem()'' method on the ''collection''.
| |
| | |
| ====Example====
| |
| | |
| ''mod/forum/classes/privacy/provider.php''
| |
| | |
| <code php>
| |
| public static function get_metadata(collection $collection) : collection {
| |
| | |
| $collection->link_subsystem(
| |
| 'core_files',
| |
| 'privacy:metadata:core_files'
| |
| );
| |
| | |
| return $collection;
| |
| }
| |
| </code>
| |
| | |
| ''mod/forum/lang/en/forum.php''
| |
| | |
| <code php>
| |
| <?php
| |
| | |
| $string['privacy:metadata:core_files'] = 'The forum stores files which have been uploaded by the user to form part of a forum post.';
| |
| </code>
| |
| | |
| ===Describing data stored in database tables===
| |
| | |
| Most Moodle plugins will store some form of user data in their own database tables.
| |
| | |
| As a plugin developer you will need to describe each database table, and each field which includes user data.
| |
| | |
| ====Example====
| |
| | |
| ''mod/forum/classes/privacy/provider.php''
| |
| | |
| <code php>
| |
| public static function get_metadata(collection $collection) : collection {
| |
| | |
| $collection->add_database_table(
| |
| 'forum_discussion_subs',
| |
| [
| |
| 'userid' => 'privacy:metadata:forum_discussion_subs:userid',
| |
| 'discussionid' => 'privacy:metadata:forum_discussion_subs:discussionid',
| |
| 'preference' => 'privacy:metadata:forum_discussion_subs:preference',
| |
| | |
| ],
| |
| 'privacy:metadata:forum_discussion_subs'
| |
| );
| |
| | |
| return $collection;
| |
| }
| |
| </code>
| |
| | |
| ''mod/forum/lang/en/forum.php''
| |
| | |
| <code php>
| |
| <?php
| |
| | |
| $string['privacy:metadata:forum_discussion_subs'] = 'Information about the subscriptions to individual forum discussions. This includes when a user has chosen to subscribe to a discussion, or to unsubscribe from one where they would otherwise be subscribed.';
| |
| $string['privacy:metadata:forum_discussion_subs:userid'] = 'The ID of the user with this subscription preference.';
| |
| $string['privacy:metadata:forum_discussion_subs:discussionid'] = 'The ID of the discussion that was subscribed to.';
| |
| $string['privacy:metadata:forum_discussion_subs:preference'] = 'The start time of the subscription.';
| |
| </code>
| |
| | |
| ===Indicating that you store site-wide user preferences===
| |
| | |
| Many plugins will include one or more user preferences. Unfortunately this is one of Moodle’s older components and many of the values stored are not pure user preferences. Each plugin should be aware of how it handles its own preferences and is best placed to determine whether they are site-wide preferences, or per-instance preferences.
| |
| | |
| Whilst most of these will have a fixed name (e.g. ''filepicker_recentrepository''), some will include a variable of some kind (e.g. ''tool_usertours_tour_completion_time_2''). Only the general name needs to be indicated rather than one copy for each preference.
| |
| | |
| Also, these should only be ''site-wide'' user preferences which do not belong to a specific Moodle context.
| |
| | |
| In the above examples:
| |
| | |
| * Preference ''filepicker_recentrepository'' belongs to the file subsystem, and is a site-wide preference affecting the user anywhere that they view the filepicker.
| |
| * Preference ''tool_usertours_tour_completion_time_2'' belongs to user tours. User tours are a site-wide feature which can affect many parts of Moodle and cross multiple contexts.
| |
| | |
| In some cases a value may be stored in the preferences table but is known to belong to a specific context within Moodle. In these cases they should be stored as metadata against that context rather than as a site-wide user preference.
| |
| | |
| You can indicate this by calling the ''add_user_preference()'' method on the ''collection''.
| |
| | |
| Any plugin providing user preferences must also implement the ''\core_privacy\local\request\preference_provider''.
| |
| | |
| ====Example====
| |
| | |
| ''admin/tool/usertours/classes/privacy/provider.php''
| |
| | |
| <code php>
| |
| public static function get_metadata(collection $collection) : collection {
| |
| | |
| $collection->add_user_preference('tool_usertours_tour_completion_time,
| |
| 'privacy:metadata:preference:tool_usertours_tour_completion_time');
| |
| | |
| return $collection;
| |
| }
| |
| </code>
| |
| | |
| ''admin/tool/usertours/lang/en/tool_usertours.php''
| |
| | |
| <code php>
| |
| <?php
| |
| | |
| $string['privacy:metadata:tool_usertours_tour_completion_time'] = 'The time that a specific user tour was last completed by a user.';
| |
| </code>
| |
| | |
| ===Indicating that you export data to an external location===
| |
| | |
| Many plugins will interact with external systems - for example cloud-based services. Often this external location is configurable within the plugin either at the site or the instance level.
| |
| | |
| As a plugin developer you will need to describe each ''type'' of target destination, alongside a list of each exported field which includes user data.
| |
| The ''actual'' destination does not need to be described as this can change based on configuration.
| |
| | |
| You can indicate this by calling the ''link_external_location()'' method on the collection.
| |
| | |
| ====Example====
| |
| | |
| ''mod/lti/classes/privacy/provider.php''
| |
| | |
| <code php>
| |
| public static function get_metadata(collection $collection) : collection {
| |
| | |
| $collection->link_external_location('lti_client', [
| |
| 'userid' => 'privacy:metadata:lti_client:userid',
| |
| 'fullname' => 'privacy:metadata:lti_client:fullname',
| |
| ], 'privacy:metadata:lti_client');
| |
| | |
| return $collection;
| |
| }
| |
| </code>
| |
| | |
| ''admin/tool/usertours/lang/en/tool_usertours.php''
| |
| | |
| <code php>
| |
| <?php
| |
| | |
| $string['privacy:metadata:lti_client'] = 'In order to integrate with a remote LTI service, user data needs to be exchanged with that service.';
| |
| $string['privacy:metadata:lti_client:userid'] = 'The userid is sent from Moodle to allow you to access your data on the remote system.';
| |
| $string['privacy:metadata:lti_client:fullname'] = 'Your full name is sent to the remote system to allow a better user experience.';
| |
| </code>
| |
| | |
| ==Providing a way to export user data==
| |
| | |
| In order to export the user data that you store, you must implement the relevant request provider.
| |
| | |
| We have named these request providers because they are called in response to a specific request from a user to access their information.
| |
| | |
| There are several different types of request provider, and you may need to implement several of these, depending on the type and nature of your plugin.
| |
| | |
| Broadly speaking plugins will fit into one of the following categories:
| |
| | |
| * Plugins which are a subplugin of another plugin. Examples include ''assignsubmission'', ''atto'', and ''datafield'';
| |
| * Plugins which are typically called by a Moodle subsystem. Examples include ''qtype'', and ''profilefield'';
| |
| * All other plugins which store data.
| |
| | |
| Most plugins will fit into this final category, whilst other plugins may fall into several categories.
| |
| Plugins which ''define'' a subplugin will also be responsible for collecting this data from their subplugins.
| |
| | |
| A final category exists - plugins which store user preferences. In some cases this may be the ''only'' provider implemented.
| |
| | |
| ===Standard plugins which store data===
| |
| | |
| A majority of Moodle plugins will fit into this category and will be required to implement the ''\core_privacy\local\request\plugin\provider'' interface. This interface requires that you define two functions:
| |
| | |
| * ''get_contexts_for_userid'' - to explain where data is held within Moodle for your plugin; and
| |
| * ''export_user_data'' - to export a user’s personal data from your plugin.
| |
| | |
| These APIs make use of the Moodle ''context'' system to hierarchically store this data.
| |
| | |
| ===Retrieving the list of contexts===
| |
| | |
| Contexts are retrieved using the ''get_contexts_for_userid'' function which takes the ID of the user being fetched, and returns a list of contexts in which the user has any data.
| |
| | |
| ''mod/forum/classes/privacy/provider.php''
| |
| | |
| <code php>
| |
| /**
| |
| * Get the list of contexts that contain user information for the specified user.
| |
| *
| |
| * @param int $userid The user to search.
| |
| * @return contextlist $contextlist The list of contexts used in this plugin.
| |
| */
| |
| public static function get_contexts_for_userid(int $userid) : contextlist {}
| |
| </code>
| |
| | |
| The function returns a ''\core_privacy\local\request\contextlist'' which is used to keep a set of contexts together in a fixed fashion.
| |
| | |
| Because a Subject Access Request covers ''every'' piece of data that is held for a user within Moodle, efficiency and performance is highly important. As a result, contexts are added to the ''contextlist'' by defining one or more SQL queries which return just the contextid. Multiple SQL queries can be added as required.
| |
| | |
| Many plugins will interact with specific subsystems and store data within them.
| |
| These subsystems will also provide a way in which to link the data that you have stored with your own database tables.
| |
| At present these are still a work in progress and only the ''core_ratings'' subsystem includes this.
| |
| | |
| | |
| | |
| ==See also==
| |
| | |
| * [[Privacy API]]
| |
| * [[:en:GDPR|GDPR for administrators]] in the user documentation
| |