* @license GNU General Public License version 2 or later; see LICENSE.txt * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace */ use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\User as TableUser; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\Component\Privacy\Administrator\Plugin\PrivacyPlugin; use Joomla\Component\Privacy\Administrator\Removal\Status; use Joomla\Component\Privacy\Administrator\Table\RequestTable; use Joomla\Database\ParameterType; use Joomla\Utilities\ArrayHelper; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; // phpcs:enable PSR1.Files.SideEffects /** * Privacy plugin managing Joomla user data * * @since 3.9.0 */ class PlgPrivacyUser extends PrivacyPlugin { /** * Application object * * @var CMSApplicationInterface * @since 4.0.0 */ protected $app; /** * Performs validation to determine if the data associated with a remove information request can be processed * * This event will not allow a super user account to be removed * * @param RequestTable $request The request record being processed * @param User $user The user account associated with this request if available * * @return Status * * @since 3.9.0 */ public function onPrivacyCanRemoveData(RequestTable $request, User $user = null) { $status = new Status(); if (!$user) { return $status; } if ($user->authorise('core.admin')) { $status->canRemove = false; $status->reason = Text::_('PLG_PRIVACY_USER_ERROR_CANNOT_REMOVE_SUPER_USER'); } return $status; } /** * Processes an export request for Joomla core user data * * This event will collect data for the following core tables: * * - #__users (excluding the password, otpKey, and otep columns) * - #__user_notes * - #__user_profiles * - User custom fields * * @param RequestTable $request The request record being processed * @param User $user The user account associated with this request if available * * @return \Joomla\Component\Privacy\Administrator\Export\Domain[] * * @since 3.9.0 */ public function onPrivacyExportRequest(RequestTable $request, User $user = null) { if (!$user) { return array(); } /** @var TableUser $userTable */ $userTable = User::getTable(); $userTable->load($user->id); $domains = array(); $domains[] = $this->createUserDomain($userTable); $domains[] = $this->createNotesDomain($userTable); $domains[] = $this->createProfileDomain($userTable); $domains[] = $this->createCustomFieldsDomain('com_users.user', array($userTable)); return $domains; } /** * Removes the data associated with a remove information request * * This event will pseudoanonymise the user account * * @param RequestTable $request The request record being processed * @param User $user The user account associated with this request if available * * @return void * * @since 3.9.0 */ public function onPrivacyRemoveData(RequestTable $request, User $user = null) { // This plugin only processes data for registered user accounts if (!$user) { return; } $pseudoanonymisedData = [ 'name' => 'User ID ' . $user->id, 'username' => bin2hex(random_bytes(12)), 'email' => 'UserID' . $user->id . 'removed@email.invalid', 'block' => true, ]; $user->bind($pseudoanonymisedData); $user->save(); // Destroy all sessions for the user account UserHelper::destroyUserSessions($user->id); } /** * Create the domain for the user notes data * * @param TableUser $user The TableUser object to process * * @return \Joomla\Component\Privacy\Administrator\Export\Domain * * @since 3.9.0 */ private function createNotesDomain(TableUser $user) { $domain = $this->createDomain('user_notes', 'joomla_user_notes_data'); $db = $this->db; $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__user_notes')) ->where($db->quoteName('user_id') . ' = :userid') ->bind(':userid', $user->id, ParameterType::INTEGER); $items = $db->setQuery($query)->loadAssocList(); // Remove user ID columns foreach (['user_id', 'created_user_id', 'modified_user_id'] as $column) { $items = ArrayHelper::dropColumn($items, $column); } foreach ($items as $item) { $domain->addItem($this->createItemFromArray($item, $item['id'])); } return $domain; } /** * Create the domain for the user profile data * * @param TableUser $user The TableUser object to process * * @return \Joomla\Component\Privacy\Administrator\Export\Domain * * @since 3.9.0 */ private function createProfileDomain(TableUser $user) { $domain = $this->createDomain('user_profile', 'joomla_user_profile_data'); $db = $this->db; $query = $db->getQuery(true) ->select('*') ->from($db->quoteName('#__user_profiles')) ->where($db->quoteName('user_id') . ' = :userid') ->order($db->quoteName('ordering') . ' ASC') ->bind(':userid', $user->id, ParameterType::INTEGER); $items = $db->setQuery($query)->loadAssocList(); foreach ($items as $item) { $domain->addItem($this->createItemFromArray($item)); } return $domain; } /** * Create the domain for the user record * * @param TableUser $user The TableUser object to process * * @return \Joomla\Component\Privacy\Administrator\Export\Domain * * @since 3.9.0 */ private function createUserDomain(TableUser $user) { $domain = $this->createDomain('users', 'joomla_users_data'); $domain->addItem($this->createItemForUserTable($user)); return $domain; } /** * Create an item object for a TableUser object * * @param TableUser $user The TableUser object to convert * * @return \Joomla\Component\Privacy\Administrator\Export\Item * * @since 3.9.0 */ private function createItemForUserTable(TableUser $user) { $data = []; $exclude = ['password', 'otpKey', 'otep']; foreach (array_keys($user->getFields()) as $fieldName) { if (!in_array($fieldName, $exclude)) { $data[$fieldName] = $user->$fieldName; } } return $this->createItemFromArray($data, $user->id); } }