Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

GDPR for plugin developers

From MoodleDocs
Revision as of 05:27, 26 March 2018 by Helen Foster (talk | contribs) (content moved from https://docs.google.com/document/d/1Y7n4Qkez4Tl83rWArOQPQCpE2NeSA2bUa8gOR2r_JFE/edit?usp=sharing - wip)

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:

  1. Detecting in which Moodle contexts the user has any data; and
  2. 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:

  1. You must create a class called provider within the namespace \your_pluginname\privacy.
  2. This class must be created at path/to/your/plugin/classes/privacy/provider.php.
  3. 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

<?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';
   }

}

block/calendar_month/lang/en/block_calendar_month.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.';

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

<?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;
   }

}

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

public static function get_metadata(collection $collection) : collection {

   $collection->link_subsystem(
       'core_files',
       'privacy:metadata:core_files'
   );
   return $collection;

}

mod/forum/lang/en/forum.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.';

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

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;

}

mod/forum/lang/en/forum.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.';

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

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;

}

admin/tool/usertours/lang/en/tool_usertours.php

<?php

$string['privacy:metadata:tool_usertours_tour_completion_time'] = 'The time that a specific user tour was last completed by a user.';

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

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;

}

admin/tool/usertours/lang/en/tool_usertours.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.';

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

   /**
    * 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 {}

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