From ca4917a1db9466bcff3721f2fb6166db01919f6c Mon Sep 17 00:00:00 2001 From: Jonas Rittershofer Date: Sun, 8 May 2022 17:25:53 +0200 Subject: [PATCH 1/4] Restrict Form Creation Signed-off-by: Jonas Rittershofer --- appinfo/info.xml | 5 + appinfo/routes.php | 15 ++- lib/Constants.php | 10 ++ lib/Controller/ApiController.php | 21 ++++- lib/Controller/ConfigController.php | 93 +++++++++++++++++++ lib/Controller/PageController.php | 11 ++- lib/Service/ConfigService.php | 129 ++++++++++++++++++++++++++ lib/Settings/Settings.php | 89 ++++++++++++++++++ lib/Settings/SettingsSection.php | 80 ++++++++++++++++ src/Forms.vue | 27 ++++-- src/FormsSettings.vue | 137 ++++++++++++++++++++++++++++ src/settings.js | 35 +++++++ templates/settings.php | 25 +++++ webpack.js | 1 + 14 files changed, 663 insertions(+), 15 deletions(-) create mode 100644 lib/Controller/ConfigController.php create mode 100644 lib/Service/ConfigService.php create mode 100644 lib/Settings/Settings.php create mode 100644 lib/Settings/SettingsSection.php create mode 100644 src/FormsSettings.vue create mode 100644 src/settings.js create mode 100644 templates/settings.php diff --git a/appinfo/info.xml b/appinfo/info.xml index 886eb0a93..863e4c35b 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -57,6 +57,11 @@ + + OCA\Forms\Settings\Settings + OCA\Forms\Settings\SettingsSection + + OCA\Forms\Activity\Filter diff --git a/appinfo/routes.php b/appinfo/routes.php index b6a8319bb..8ebe6478b 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -25,13 +25,25 @@ return [ 'routes' => [ + // Internal AppConfig routes + [ + 'name' => 'config#getAppConfig', + 'url' => '/config', + 'verb' => 'GET' + ], + [ + 'name' => 'config#updateAppConfig', + 'url' => '/config/update', + 'verb' => 'POST' + ], + // Public Share Link [ 'name' => 'page#public_link_view', 'url' => '/s/{hash}', 'verb' => 'GET' - ], + // Internal views [ 'name' => 'page#views', @@ -51,6 +63,7 @@ 'verb' => 'GET' ], ], + 'ocs' => [ // Forms diff --git a/lib/Constants.php b/lib/Constants.php index 6d0763091..13f16c5bb 100644 --- a/lib/Constants.php +++ b/lib/Constants.php @@ -26,6 +26,16 @@ use OCP\Share\IShare; class Constants { + /** + * Used AppConfig Keys + */ + public const CONFIG_KEY_CREATIONALLOWEDGROUPS = 'creationAllowedGroups'; + public const CONFIG_KEY_RESTRICTCREATION = 'restrictCreation'; + public const CONFIG_KEYS = [ + self::CONFIG_KEY_CREATIONALLOWEDGROUPS, + self::CONFIG_KEY_RESTRICTCREATION + ]; + /** * Maximum String lengths, the database is set to store. */ diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index f88f5e868..f6c9eb9fd 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -40,6 +40,7 @@ use OCA\Forms\Db\ShareMapper; use OCA\Forms\Db\Submission; use OCA\Forms\Db\SubmissionMapper; +use OCA\Forms\Service\ConfigService; use OCA\Forms\Service\FormsService; use OCA\Forms\Service\SubmissionService; @@ -84,6 +85,9 @@ class ApiController extends OCSController { /** @var SubmissionMapper */ private $submissionMapper; + /** @var ConfigService */ + private $configService; + /** @var FormsService */ private $formsService; @@ -113,6 +117,7 @@ public function __construct(string $appName, QuestionMapper $questionMapper, ShareMapper $shareMapper, SubmissionMapper $submissionMapper, + ConfigService $configService, FormsService $formsService, SubmissionService $submissionService, IL10N $l10n, @@ -130,6 +135,7 @@ public function __construct(string $appName, $this->questionMapper = $questionMapper; $this->shareMapper = $shareMapper; $this->submissionMapper = $submissionMapper; + $this->configService = $configService; $this->formsService = $formsService; $this->submissionService = $submissionService; @@ -242,15 +248,20 @@ public function getForm(int $id): DataResponse { * @throws OCSForbiddenException */ public function newForm(): DataResponse { - $form = new Form(); + // Check if user is allowed + if (!$this->configService->canCreateForms()) { + $this->logger->debug('This user is not allowed to create Forms.'); + throw new OCSForbiddenException(); + } + // Create Form + $form = new Form(); $form->setOwnerId($this->currentUser->getUID()); $form->setCreated(time()); $form->setHash($this->secureRandom->generate( 16, ISecureRandom::CHAR_HUMAN_READABLE )); - $form->setTitle(''); $form->setDescription(''); $form->setAccess([ @@ -280,6 +291,12 @@ public function cloneForm(int $id): DataResponse { 'id' => $id ]); + // Check if user can create forms + if (!$this->configService->canCreateForms()) { + $this->logger->debug('This user is not allowed to create Forms.'); + throw new OCSForbiddenException(); + } + try { $oldForm = $this->formMapper->findById($id); } catch (IMapperException $e) { diff --git a/lib/Controller/ConfigController.php b/lib/Controller/ConfigController.php new file mode 100644 index 000000000..f27a3f276 --- /dev/null +++ b/lib/Controller/ConfigController.php @@ -0,0 +1,93 @@ + + * + * @author Jonas Rittershofer + * + * @license AGPL-3.0-or-later + * + * 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\Forms\Controller; + +use OCA\Forms\Constants; +use OCA\Forms\Service\ConfigService; +use OCP\AppFramework\ApiController; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\IConfig; +use OCP\ILogger; +use OCP\IRequest; + +class ConfigController extends ApiController { + protected $appName; + + /** @var ConfigService */ + private $configService; + + /** @var IConfig */ + private $config; + + /** @var ILogger */ + private $logger; + + public function __construct(string $appName, + ConfigService $configService, + IConfig $config, + ILogger $logger, + IRequest $request) { + parent::__construct($appName, $request); + $this->appName = $appName; + $this->configService = $configService; + $this->config = $config; + $this->logger = $logger; + } + + /** + * Get the current AppConfig + * @return DataResponse + */ + public function getAppConfig(): DataResponse { + return new DataResponse($this->configService->getAppConfig()); + } + + /** + * Update values on appConfig. + * Admin required, thus not checking separately. + * + * @param string $configKey AppConfig Key to store + * @param mixed $configValues Corresponding AppConfig Value + * + */ + public function updateAppConfig(string $configKey, $configValue): DataResponse { + $this->logger->debug('Updating AppConfig: {configKey} => {configValue}', [ + 'configKey' => $configKey, + 'configValue' => $configValue + ]); + + // Check for allowed keys + if (!in_array($configKey, Constants::CONFIG_KEYS)) { + return new DataResponse('Unknown appConfig key: ' . $configKey, Http::STATUS_BAD_REQUEST); + } + + // Set on DB + $this->config->setAppValue($this->appName, $configKey, json_encode($configValue)); + + return new DataResponse(); + } +} diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 2bc19f131..3f293a4ae 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -30,6 +30,7 @@ use OCA\Forms\Db\Form; use OCA\Forms\Db\FormMapper; use OCA\Forms\Db\ShareMapper; +use OCA\Forms\Service\ConfigService; use OCA\Forms\Service\FormsService; use OCP\Accounts\IAccountManager; @@ -64,15 +65,18 @@ class PageController extends Controller { /** @var ShareMapper */ private $shareMapper; + /** @var ConfigService */ + private $configService; + /** @var FormsService */ private $formsService; /** @var IAccountManager */ protected $accountManager; - + /** @var IGroupManager */ private $groupManager; - + /** @var IInitialStateService */ private $initialStateService; @@ -98,6 +102,7 @@ public function __construct(string $appName, IRequest $request, FormMapper $formMapper, ShareMapper $shareMapper, + ConfigService $configService, FormsService $formsService, IAccountManager $accountManager, IGroupManager $groupManager, @@ -113,6 +118,7 @@ public function __construct(string $appName, $this->formMapper = $formMapper; $this->shareMapper = $shareMapper; + $this->configService = $configService; $this->formsService = $formsService; $this->accountManager = $accountManager; @@ -137,6 +143,7 @@ public function index(): TemplateResponse { Util::addStyle($this->appName, 'forms'); $this->insertHeaderOnIos(); $this->initialStateService->provideInitialState($this->appName, 'maxStringLengths', Constants::MAX_STRING_LENGTHS); + $this->initialStateService->provideInitialState($this->appName, 'appConfig', $this->configService->getAppConfig()); return new TemplateResponse($this->appName, self::TEMPLATE_MAIN); } diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php new file mode 100644 index 000000000..f45c0f984 --- /dev/null +++ b/lib/Service/ConfigService.php @@ -0,0 +1,129 @@ + + * + * @author Jonas Rittershofer + * + * @license AGPL-3.0-or-later + * + * 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\Forms\Service; + +use OCA\Forms\Constants; + +use OCP\IConfig; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\ILogger; +use OCP\IUser; +use OCP\IUserSession; + +class ConfigService { + protected $appName; + + /** @var IConfig */ + private $config; + + /** @var IGroupManager */ + private $groupManager; + + /** @var ILogger */ + private $logger; + + /** @var IUser */ + private $currentUser; + + public function __construct(string $appName, + IConfig $config, + IGroupManager $groupManager, + ILogger $logger, + IUserSession $userSession) { + $this->appName = $appName; + $this->config = $config; + $this->groupManager = $groupManager; + $this->logger = $logger; + + $this->currentUser = $userSession->getUser(); + } + + /** + * Load the single values, decode, have default values + */ + private function getUnformattedCreationAllowedGroups(): array { + return json_decode($this->config->getAppValue($this->appName, Constants::CONFIG_KEY_CREATIONALLOWEDGROUPS, "[]")); + } + public function getCreationAllowedGroups(): array { + return $this->formatGroupsForMultiselect($this->getUnformattedCreationAllowedGroups()); + } + public function getRestrictCreation(): bool { + return json_decode($this->config->getAppValue($this->appName, Constants::CONFIG_KEY_RESTRICTCREATION, "false")); + } + + /** + * Provide the full AppConfig + */ + public function getAppConfig(): array { + return [ + Constants::CONFIG_KEY_CREATIONALLOWEDGROUPS => $this->getCreationAllowedGroups(), + Constants::CONFIG_KEY_RESTRICTCREATION => $this->getRestrictCreation(), + + // Additional, calculated information out of Config + 'canCreateForms' => $this->canCreateForms() + ]; + } + + /** + * Format the stored groups + * + * @param String[] $groups String Array of the groupIds + * @return Array[] Array of GroupObjects + */ + private function formatGroupsForMultiselect(array $groups): array { + $formattedGroups = []; + foreach ($groups as $groupId) { + $group = $this->groupManager->get($groupId); + if ($group instanceof IGroup) { + $formattedGroups[] = [ + 'groupId' => $groupId, + 'displayName' => $group->getDisplayName() + ]; + } + } + return $formattedGroups; + } + + /** + * Check if currentUser is allowed to create Forms + * @return bool + */ + public function canCreateForms(): bool { + // Restriction active or not + if (!$this->getRestrictCreation()) { + return true; + } + + $userGroups = $this->groupManager->getUserGroupIds($this->currentUser); + // If array intersection is not empty, user is member of any allowed group. + if (sizeof(array_intersect($userGroups, $this->getUnformattedCreationAllowedGroups()))) { + return true; + } + + return false; + } +} diff --git a/lib/Settings/Settings.php b/lib/Settings/Settings.php new file mode 100644 index 000000000..a1da622f8 --- /dev/null +++ b/lib/Settings/Settings.php @@ -0,0 +1,89 @@ + + * + * @author Jonas Rittershofer + * + * @license AGPL-3.0-or-later + * + * 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\Forms\Settings; + +use OCA\Forms\Service\ConfigService; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IGroupManager; +use OCP\IInitialStateService; +use OCP\Settings\ISettings; +use OCP\Util; + +class Settings implements ISettings { + private $appName; + + /** @var ConfigService */ + private $configService; + + /** @var IGroupManager */ + private $groupManager; + + /** @var IInitialStateService */ + private $initialStateService; + + public function __construct(string $appName, + ConfigService $configService, + IGroupManager $groupManager, + IInitialStateService $initialStateService) { + $this->appName = $appName; + $this->configService = $configService; + $this->groupManager = $groupManager; + $this->initialStateService = $initialStateService; + } + + /** + * Provide all available Groups + * + * @return Array[] Array of GroupObjects + */ + private function getAvailableGroups(): array { + $formattedGroups = []; + $groups = $this->groupManager->search(''); + foreach ($groups as $group) { + $formattedGroups[] = [ + 'groupId' => $group->getGID(), + 'displayName' => $group->getDisplayName() + ]; + } + return $formattedGroups; + } + + public function getForm(): TemplateResponse { + Util::addScript($this->appName, 'forms-settings'); + $this->initialStateService->provideInitialState($this->appName, 'availableGroups', $this->getAvailableGroups()); + $this->initialStateService->provideInitialState($this->appName, 'appConfig', $this->configService->getAppConfig()); + + return new TemplateResponse($this->appName, 'settings'); + } + + public function getSection(): string { + return 'forms'; + } + + public function getPriority(): int { + return 50; + } +} diff --git a/lib/Settings/SettingsSection.php b/lib/Settings/SettingsSection.php new file mode 100644 index 000000000..2bd02e84b --- /dev/null +++ b/lib/Settings/SettingsSection.php @@ -0,0 +1,80 @@ + + * + * @author Jonas Rittershofer + * + * @license AGPL-3.0-or-later + * + * 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\Forms\Settings; + +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\Settings\IIconSection; + +class SettingsSection implements IIconSection { + + /** @var IL10N */ + private $l10n; + + /** @var IURLGenerator */ + private $urlGenerator; + + public function __construct(IL10N $l10n, IURLGenerator $urlGenerator) { + $this->l10n = $l10n; + $this->urlGenerator = $urlGenerator; + } + + /** + * Section ID to be used for Setting + * + * @return string + */ + public function getID(): string { + return 'forms'; + } + + /** + * Translated Name to display + * + * @return string + */ + public function getName(): string { + return $this->l10n->t('Forms'); + } + + /** + * Priority of the Section. Using Priority here as on Navigationorder. + * + * @return int between 0-99 + */ + public function getPriority(): int { + return 77; + } + + /** + * Section Icon + * + * @return string Relative Path to the icon + */ + public function getIcon(): string { + return $this->urlGenerator->imagePath('forms', 'forms-dark.svg'); + } +} diff --git a/src/Forms.vue b/src/Forms.vue index e9660ed22..b50d3a77c 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -23,8 +23,11 @@