J3.x

Erweiterungen mit der Datenschutz-Komponente integrieren

From Joomla! Documentation

Revision as of 04:39, 19 March 2020 by Blancanieve (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Other languages:
Deutsch • ‎English • ‎Nederlands • ‎français • ‎italiano

Die auf dieser Seite gezeigten Informationen sind an Entwickler von Erweiterungen gerichtet, um sie bei der Integration der Datenschutz Tools zu unterstützen.

Möglichkeiten von Erweiterungen bezüglich des Datenschutzes

Die neue Datenschutzkomponente verfügt über einen Abschnitt Datenschutzhinweise, in dem datenschutzbezogene Funktionen von Erweiterungen angezeigt werden. Damit soll den Benutzern verständlich gemacht werden, welche Funktionen eine Erweiterung in Bezug auf personenbezogene Nutzerdaten besitzt. Den Website-Betreibern hilft es, entsprechend zu planen. Details zu diesem Abschnitt, einschließlich der Frage, wie ein Plugin damit integriert werden muss, sind auf der Seite Bericht von Erweiterungsmöglichkeiten an die Datenschutzkomponente zu finden.

Prüfung auf eine veröffentlichte Datenschutzerklärung

Die Prüfung durch die Datenschutzkomponente informiert die Benutzer, ob bereits eine Datenschutzerklärung auf der Website veröffentlicht ist. Diese Prüfung wird durch das Ereignis onPrivacyCheckPrivacyPolicyPublished durchgeführt, das von Plugins in den Plugin-Gruppen privacy, system und user abgefragt werden kann. Das Ereignis erhält ein assoziatives Array durch Referenz mit zwei Schlüsseln als Argument:

  • published - Ein boolescher Wert, der anzeigt, ob es bereits eine veröffentlichte Richtlinie gibt.
  • editLink - Die URL zur Bearbeitung des Richtlinienelements, die angezeigt wird, wenn eine Datenschutzerklärung veröffentlicht wurde.
  • articlePublished - Gibt an, ob der Beitrag veröffentlicht ist, wenn er in der Datenbank existiert.

Als Best Practice wird empfohlen, dass Plugins zunächst prüfen, ob published bereits auf true gesetzt ist. In diesem Fall werden keine weiteren Änderungen am Datenarray vorgenommen. Beachte, dass dieses Ereignis von dem Joomla-eigenem Plugin System - Datenschutz Zustimmung verarbeitet wird.

public function onPrivacyCheckPrivacyPolicyPublished(&$policy)
{
	// If another plugin has already indicated a policy is published, we won't change anything here
	if ($policy['published'])
	{
		return;
	}

	// Do stuff to find the privacy policy data

	// For core, we check if the article exists in the database and is published or not
	$query = $this->db->getQuery(true)
		->select($this->db->quoteName(array('id', 'state')))
		->from($this->db->quoteName('#__content'))
		->where($this->db->quoteName('id') . ' = ' . (int) $articleId);
	$this->db->setQuery($query);

	$article = $this->db->loadObject();

	// Check if the article exists
	if (!$article)
	{
		return;
	}

	// Check if the article is published
	if ($article->state == 1)
	{
		$policy['articlePublished'] = true;
	}

	$policy['published'] = true;
	$policy['editLink']  = ''; // The link to the item's edit page, processed through JRoute, i.e. JRoute::_('index.php?option=com_content&task=article.edit&id=1');
}


Daten zum Datenexport der Informationsanfrage hinzufügen

Es gibt mehrere Helfer-Methoden in der Klasse PrivacyPlugin und es wird empfohlen, dass Datenschutz-Plugins diese Klasse erweitern, um diese Helfer zu erben (dies ist vergleichbar mit der Klasse FinderIndexerAdapter für Smart Search-Plugins als Beispiel).

JLoader::register('PrivacyPlugin', JPATH_ADMINISTRATOR . '/components/com_privacy/helpers/plugin.php');

class PlgPrivacyContent extends PrivacyPlugin {}

Um Daten zu einer Exportanforderung hinzuzufügen, muss ein Plugin in den Gruppen privacy oder system das Ereignis onPrivacyExportRequest abfragen.

Das Ereignis empfängt zwei Argumente:

  • $request - Ein Objekt des Typs PrivacyTableRequest, das den Datensatz für die Informationsanforderung aus der Datenbank enthält.
  • $user - Wenn es ein Konto für die E-Mail-Adresse der Informationsanfrage gibt, wird ein Joomla\CMS\User\User\User-Objekt angegeben, das die Daten des Benutzerkontos enthält.

Das Ereignis muss ein Array von PrivacyExportDomain-Objekten zurückgeben, das die zu exportierenden Daten für eine bestimmte Domain enthält. Wenn das Plugin keine Daten hat, muss es ein leeres Array zurückgeben.

Ein PrivacyExportDomain-Datenobjekt repräsentiert typischerweise die Daten, die in einer Tabelle der Datenbank gefunden wurden, in einem Datenbereich (domain) und besteht aus drei Elementen:

  • Name des Bereichs - Ein Name zur Identifizierung des Datenbereichs
  • Beschreibung des Bereichs - Eine kurze Beschreibung der im Datenbereich enthaltenen Daten
  • Elemente des Bereichs - Ein Array von PrivacyExportItem-Datenobjekten, der alle Elemente innerhalb des Datenbereichs enthält.

Ein PrivacyExportItem-Datenobjekt stellt ein einzelnes Element innerhalb des Datenbereichs dar und besteht aus zwei Elementen:

  • Element-ID - Die primäre Kennung für dieses Element innerhalb des Bereichs, in der Regel ist dies der Primärschlüssel des Datenbankeintrags.
  • Elementfelder - Ein Array von PrivacyExportField-Datenobjekten, die jedes Feld für das Element enthalten

Das Datenobjekt PrivacyExportField stellt ein einzelnes Feld innerhalb eines Elements dar und besteht aus zwei Elementen:

  • Feldname - Der Name des Feldes
  • Feldwert - Der Wert des Feldes

Der allgemeine Workflow für den Exportprozess ist folgender:

  • Validieren, ob das Plugin tatsächlich Daten verarbeiten soll.
  • Abfrage der Daten aus der Datenbank
  • Erstellung eines Bereichs für die Ergebnisse (die Methode createDomain, die von der Klasse PrivacyPlugin geerbt wurde, kann dabei helfen)
  • Hinzufügen von Elementen für jede Zeile
    • Die von der Klasse PrivacyPlugin-Klasse geerbte Methode createItemFromArray kann verwendet werden, um ein PrivacyExportItem-Objekt aus einem Array zu erstellen (dies sollte in Verbindung mit der Methode loadAssocList der Datenbank verwendet werden).
    • Die Methode createItemForTable, die von der Klasse PrivacyPlugin geerbt wurde, kann verwendet werden, um ein PrivacyExportItem-Objekt aus einem Joomla-CMS-Table-Objekt zu erstellen.
  • Rückgabe des Datenbereichs

Unten ist ein Beispiel für den Export von Artikeln zu sehen, die von einem Benutzer erstellt wurden, einschließlich benutzerdefinierter Felddaten.

use Joomla\CMS\User\User;

JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php');
JLoader::register('PrivacyPlugin', JPATH_ADMINISTRATOR . '/components/com_privacy/helpers/plugin.php');

class PlgPrivacyContent extends PrivacyPlugin
{
	/**
	 * @var  JDatabaseDriver
	 */
	protected $db;

	/**
	 * @var  array
	 */
	protected $contents = array();

	public function onPrivacyExportRequest(PrivacyTableRequest $request, User $user = null)
	{
		// This plugin only processes data for registered user accounts
		if (!$user)
		{
			return array();
		}

		$domains   = array();
		$domains[] = $this->createContentDomain($user);

		// Create domains for each article's custom fields
		foreach ($this->contents as $content)
		{
			$domains[] = $this->createContentCustomFieldsDomain($content);
		}

		return $domains;
	}

	private function createContentDomain(User $user)
	{
		$domain = $this->createDomain('user_content', 'joomla_user_content_data');

		$query = $this->db->getQuery(true)
			->select('*')
			->from($this->db->quoteName('#__content'))
			->where($this->db->quoteName('created_by') . ' = ' . (int) $user->id)
			->order($this->db->quoteName('ordering') . ' ASC');

		$items = $this->db->setQuery($query)->loadAssocList();

		// Add each article to the domain
		foreach ($items as $item)
		{
			$domain->addItem($this->createItemFromArray($item));

			// Store the article for use in the custom fields processing
			$this->contents[] = (object) $item;
		}

		return $domain;
	}

	private function createContentCustomFieldsDomain($content)
	{
		$domain = $this->createDomain('content_custom_fields', 'joomla_content_custom_fields_data');

		// Get item's fields, also preparing their value property for manual display
		$fields = FieldsHelper::getFields('com_content.article', $content);

		foreach ($fields as $field)
		{
			$fieldValue = is_array($field->value) ? implode(', ', $field->value) : $field->value;

			$data = array(
				'content_id'  => $content->id,
				'field_name'  => $field->name,
				'field_title' => $field->title,
				'field_value' => $fieldValue,
			);

			$domain->addItem($this->createItemFromArray($data));
		}

		return $domain;
	}
}


Verarbeitung von Löschanfragen

Die Bearbeitung eines Löschantrags erfordert zwei Schritte: Die Bestätigung, dass die Daten der betroffenen Person entfernt werden können, und die tatsächliche Entfernung. Auch hier wird vorgeschlagen, dass Plugins die Klasse PrivacyPlugin erweitern, dies ist jedoch keine strenge Anforderung.

JLoader::register('PrivacyPlugin', JPATH_ADMINISTRATOR . '/components/com_privacy/helpers/plugin.php');

class PlgPrivacyUser extends PrivacyPlugin {}

Validierung, dass Daten gelöscht werden können

Wenn das Plugin die Daten einer Person blockieren muss, muss es das Ereignis onPrivacyCanRemoveData abfragen. Im Allgemeinen sollte eine Löschung blockiert werden, wenn es triftige rechtliche Gründe für die Aufbewahrung der Daten gibt oder wenn die Löschung zu dauerhaften Schäden an der Website führen kann (z. B. verbietet das Plugin Datenschutz - Benutzerkonten die Entfernung eines Superuser-Kontos).

Das Ereignis empfängt zwei Argumente:

  • $request - Ein Objekt des Typs PrivacyTableRequest, das den Datensatz für die Informationsanforderung aus der Datenbank enthält.
  • $user - Wenn es ein Konto für die E-Mail-Adresse der Informationsanfrage gibt, wird ein Joomla\CMS\User\User\User-Objekt angegeben, das die Daten des Benutzerkontos enthält.

Das Ereignis muss ein PrivacyRemovalStatus-Datenobjekt zurückgeben, das angibt, ob die Daten gelöscht werden können und, falls nein, einen Grund die Nicht-Löschung nennt.

use Joomla\CMS\Language\Text;
use Joomla\CMS\User\User;

JLoader::register('PrivacyPlugin', JPATH_ADMINISTRATOR . '/components/com_privacy/helpers/plugin.php');
JLoader::register('PrivacyRemovalStatus', JPATH_ADMINISTRATOR . '/components/com_privacy/helpers/removal/status.php');

class PlgPrivacyUser extends PrivacyPlugin
{
	public function onPrivacyCanRemoveData(PrivacyTableRequest $request, User $user = null)
	{
		$status = new PrivacyRemovalStatus;

		// We only need to check if there is an associated user account
		if (!$user)
		{
			return $status;
		}

		// We will not remove a super user account from the site
		if ($user->authorise('core.admin'))
		{
			$status->canRemove = false;
			$status->reason    = Text::_('PLG_PRIVACY_USER_ERROR_CANNOT_REMOVE_SUPER_USER');
		}

		return $status;
	}
}

Daten entfernen oder anonymisieren

Wenn das Plugin die Löschung oder Anonymisierung von Daten übernimmt, muss es das Ereignis onPrivacyRemoveData abonnieren.

Das Ereignis empfängt zwei Argumente:

  • $request - Ein Objekt des Typs PrivacyTableRequest, das den Datensatz für die Informationsanforderung aus der Datenbank enthält.
  • $user - Wenn es ein Konto für die E-Mail-Adresse der Informationsanfrage gibt, wird ein Joomla\CMS\User\User\User-Objekt angegeben, das die Daten des Benutzerkontos enthält.

Im Gegensatz zu den anderen Ereignissen in dieser Komponente wird von den Plugins nicht erwartet, dass sie Daten zurückgeben.

Abhängig von den Anforderungen der zugrunde liegenden Daten sollten Plugins Informationen, die aufbewahrt werden, anonymisieren und nicht mehr benötigte Daten löschen. Falls erforderlich, kann das Plugin andere Aktionen im Zusammenhang mit der Anfrage und/oder dem Benutzerkonto durchführen.

Das folgende Beispiel zeigt, wie Joomla Daten, die mit dem Joomla-Benutzerkonto in Verbindung stehen, anonymisiert und löscht.

use Joomla\CMS\Factory;
use Joomla\CMS\User\User;

JLoader::register('PrivacyPlugin', JPATH_ADMINISTRATOR . '/components/com_privacy/helpers/plugin.php');

class PlgPrivacyUser extends PrivacyPlugin
{
	/**
	 * @var  JDatabaseDriver
	 */
	protected $db;

	public function onPrivacyRemoveData(PrivacyTableRequest $request, User $user = null)
	{
		// This plugin only processes data for registered user accounts
		if (!$user)
		{
			return;
		}

		$pseudoanonymisedData = array(
			'name'      => 'User ID ' . $user->id,
			'username'  => bin2hex(random_bytes(12)), // Generates a random username
			'email'     => 'UserID' . $user->id . 'removed@email.invalid',
			'block'     => true,
		);

		$user->bind($pseudoanonymisedData);

		$user->save();

		// Destroy all sessions for the user account
		$sessionIds = $this->db->setQuery(
			$this->db->getQuery(true)
				->select($this->db->quoteName('session_id'))
				->from($this->db->quoteName('#__session'))
				->where($this->db->quoteName('userid') . ' = ' . (int) $user->id)
		)->loadColumn();

		// If there aren't any active sessions then there's nothing to do here
		if (empty($sessionIds))
		{
			return;
		}

		$storeName = Factory::getConfig()->get('session_handler', 'none');
		$store     = JSessionStorage::getInstance($storeName);
		$quotedIds = array();

		// Destroy the sessions and quote the IDs to purge the session table
		foreach ($sessionIds as $sessionId)
		{
			$store->destroy($sessionId);
			$quotedIds[] = $this->db->quote($sessionId);
		}

		$this->db->setQuery(
			$this->db->getQuery(true)
				->delete($this->db->quoteName('#__session'))
				->where($this->db->quoteName('session_id') . ' IN (' . implode(', ', $quotedIds) . ')')
		)->execute();
	}
}