diff --git a/appinfo/routes.php b/appinfo/routes.php new file mode 100644 index 0000000..a390438 --- /dev/null +++ b/appinfo/routes.php @@ -0,0 +1,29 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +return [ + 'ocs' => [ + ['name' => 'DataRequest#export', 'url' => '/api/v1/export', 'verb' => 'POST'], + ['name' => 'DataRequest#deletion', 'url' => '/api/v1/deletion', 'verb' => 'POST'], + ] +]; diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..83e4e92 --- /dev/null +++ b/js/app.js @@ -0,0 +1,58 @@ +/** + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +'use strict'; + +(function(OCA) { + OCA.DataRequest = OCA.DataRequest || {}; + + OCA.DataRequest.App = { + init: function() { + $('#data-request button').on('click', function() { + OCA.DataRequest.App.request($(this)); + }); + }, + + request: function ($context) { + if(OC.PasswordConfirmation.requiresPasswordConfirmation()) { + var self = this; + OC.PasswordConfirmation.requirePasswordConfirmation(function () { + self._doRequest($context); + }); + return; + } + this._doRequest($context); + }, + + _doRequest($context) { + $context.prop('disabled', 'disabled'); + $.ajax({ + type: 'POST', + url: OC.linkToOCS('apps/data_request/api/v1', 2) + $context.data('request'), + success: function () { + $context.html($context.html() + ' ' + t('data_request', 'sent!')); + }, + error: function () { + $context.prop('disabled', ''); + } + }); + } + }; +})(OCA); diff --git a/js/init.js b/js/init.js new file mode 100644 index 0000000..09cd06c --- /dev/null +++ b/js/init.js @@ -0,0 +1,25 @@ +/** + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +'use strict'; + +$(document).ready(function() { + OCA.DataRequest.App.init(); +}); diff --git a/lib/Controller/DataRequestController.php b/lib/Controller/DataRequestController.php new file mode 100644 index 0000000..ddad16c --- /dev/null +++ b/lib/Controller/DataRequestController.php @@ -0,0 +1,80 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\DataRequest\Controller; + +use OCA\DataRequest\Services\Request; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCSController; +use OCP\IRequest; + +class DataRequestController extends OCSController { + + /** @var Request */ + private $dataRequest; + + public function __construct( + $appName, + IRequest $request, + $corsMethods = 'PUT, POST, GET, DELETE, PATCH', + $corsAllowedHeaders = 'Authorization, Content-Type, Accept', + $corsMaxAge = 1728000, + Request $dataRequest + ) { + parent::__construct($appName, $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge); + $this->dataRequest = $dataRequest; + } + + /** + * @NoAdminRequired + * @PasswordConfirmationRequired + */ + public function export() { + try { + $this->dataRequest->sendExportRequest(); + return new DataResponse(); + } catch(\RuntimeException $e) { + return new DataResponse( + ['error' => $e->getMessage()], + Http::STATUS_INTERNAL_SERVER_ERROR + ); + } + } + + /** + * @NoAdminRequired + * @PasswordConfirmationRequired + */ + public function deletion() { + try { + $this->dataRequest->sendDeleteRequest(); + return new DataResponse(); + } catch(\RuntimeException $e) { + return new DataResponse( + ['error' => $e->getMessage()], + Http::STATUS_INTERNAL_SERVER_ERROR + ); + } + } +} diff --git a/lib/Services/Request.php b/lib/Services/Request.php new file mode 100644 index 0000000..3c43364 --- /dev/null +++ b/lib/Services/Request.php @@ -0,0 +1,150 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\DataRequest\Services; + +use OCP\Defaults; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserSession; +use OCP\L10N\IFactory; +use OCP\Mail\IEMailTemplate; +use OCP\Mail\IMailer; +use OCP\Util; + +class Request { + /** @var string */ + protected $defaultLanguage; + /** @var IGroupManager */ + private $groupManager; + /** @var IMailer */ + private $mailer; + /** @var IFactory */ + private $l10nFactory; + /** @var IConfig */ + private $config; + /** @var IUser */ + private $requester; + + public function __construct(IGroupManager $groupManager, IMailer $mailer, IFactory $l10nFactory, IConfig $config, IUserSession $userSession) { + $this->groupManager = $groupManager; + $this->mailer = $mailer; + $this->l10nFactory = $l10nFactory; + $this->config = $config; + $this->requester = $userSession->getUser(); + } + + public function sendExportRequest() { + $this->sendRequest(function (IUser $r) {return $this->getExportTemplate($r); }); + } + + public function sendDeleteRequest() { + $this->sendRequest(function (IUser $r) {return $this->getDeletionTemplate($r); }); + } + + protected function sendRequest(callable $templateGenerator) { + $admins = $this->getAdmins(); + + $oneMailSent = false; + foreach ($admins as $admin) { + $template = $templateGenerator($admin); + if($this->craftEmailTo($admin, $template) === true) { + $oneMailSent = true; + } + } + if(!$oneMailSent) { + throw new \RuntimeException('No mail was sent successfully'); + } + + } + + protected function getDefaultLang() { + if($this->defaultLanguage === null) { + $this->defaultLanguage = $this->config->getSystemValue('default_language', 'en'); + } + return $this->defaultLanguage; + } + + protected function craftEmailTo(IUser $admin, IEMailTemplate $template) { + $defaults = new Defaults(); + $senderAddress = $this->requester->getEMailAddress() ?: Util::getDefaultEmailAddress('no-reply'); + $senderName = $this->requester->getEMailAddress() ? $this->requester->getDisplayName() : $defaults->getName(); + + $message = $this->mailer->createMessage(); + $message->setTo([$admin->getEMailAddress() => $admin->getDisplayName()]); + $message->setSubject($template->renderSubject()); + $message->setHtmlBody($template->renderHtml()); + $message->setPlainBody($template->renderText()); + $message->setFrom([$senderAddress => $senderName]); + + try { + $this->mailer->send($message); + } catch (\Exception $e) { + return $e; + } + + return true; + } + + protected function getExportTemplate(IUser $admin) { + $l = $this->l10nFactory->get('data_request', $this->config->getUserValue($admin->getUID(), 'core', 'lang', $this->getDefaultLang())); + $template = $this->mailer->createEMailTemplate('data_request.Export', []); + + $template->setSubject($l->t('Personal data export request')); + + $template->addHeader(); + $template->addHeading($l->t('Hello %s,',[$admin->getDisplayName()])); + $template->addBodyText($l->t('The user %s, identified by user id "%s", has requested an export of his personal data. Please take action accordingly.', [$this->requester->getDisplayName(), $this->requester->getUID()])); + + $template->addFooter(); + + return $template; + } + + protected function getDeletionTemplate(IUser $admin) { + $l = $this->l10nFactory->get('data_request', $this->config->getUserValue($admin->getUID(), 'core', 'lang', $this->getDefaultLang())); + $template = $this->mailer->createEMailTemplate('data_request.Deletion', []); + + $template->setSubject($l->t('Account deletion request')); + + $template->addHeader(); + $template->addHeading($l->t('Hello %s,',[$admin->getDisplayName()])); + $template->addBodyText($l->t('The user %s, identified by user id "%s", has requested to delete the account. Please take action accordingly.'), [$this->requester->getDisplayName(), $this->requester->getUID()]); + + $template->addFooter(); + + return $template; + } + + protected function getAdmins() { + $admins = $this->groupManager->get('admin')->searchUsers(''); + $admins = array_filter($admins, function(IUser $admin) { + return $admin->getEMailAddress() !== null; + }); + if(empty($admins)) { + throw new \RuntimeException('No admin has entered an email address'); + } + return $admins; + } +} diff --git a/templates/Settings/personal.php b/templates/Settings/personal.php index 3d0b694..dc53520 100644 --- a/templates/Settings/personal.php +++ b/templates/Settings/personal.php @@ -22,15 +22,17 @@ */ style('data_request', 'style'); +script('data_request', ['init', 'app']); + ?>

t('Personal data requests')); ?>

- +
- +