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..f0827e060 100644 --- a/lib/Constants.php +++ b/lib/Constants.php @@ -26,6 +26,20 @@ use OCP\Share\IShare; class Constants { + /** + * Used AppConfig Keys + */ + public const CONFIG_KEY_ALLOWPERMITALL = 'allowPermitAll'; + public const CONFIG_KEY_ALLOWPUBLICLINK = 'allowPublicLink'; + public const CONFIG_KEY_CREATIONALLOWEDGROUPS = 'creationAllowedGroups'; + public const CONFIG_KEY_RESTRICTCREATION = 'restrictCreation'; + public const CONFIG_KEYS = [ + self::CONFIG_KEY_ALLOWPERMITALL, + self::CONFIG_KEY_ALLOWPUBLICLINK, + 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/Controller/ShareApiController.php b/lib/Controller/ShareApiController.php index 3dd19431d..5db2276df 100644 --- a/lib/Controller/ShareApiController.php +++ b/lib/Controller/ShareApiController.php @@ -31,6 +31,7 @@ use OCA\Forms\Db\FormMapper; use OCA\Forms\Db\Share; use OCA\Forms\Db\ShareMapper; +use OCA\Forms\Service\ConfigService; use OCA\Forms\Service\FormsService; use OCP\AppFramework\OCSController; @@ -59,6 +60,9 @@ class ShareApiController extends OCSController { /** @var ShareMapper */ private $shareMapper; + /** @var ConfigService */ + private $configService; + /** @var FormsService */ private $formsService; @@ -80,6 +84,7 @@ class ShareApiController extends OCSController { public function __construct(string $appName, FormMapper $formMapper, ShareMapper $shareMapper, + ConfigService $configService, FormsService $formsService, IGroupManager $groupManager, ILogger $logger, @@ -91,6 +96,7 @@ public function __construct(string $appName, $this->appName = $appName; $this->formMapper = $formMapper; $this->shareMapper = $shareMapper; + $this->configService = $configService; $this->formsService = $formsService; $this->groupManager = $groupManager; $this->logger = $logger; @@ -125,6 +131,12 @@ public function newShare(int $formId, int $shareType, string $shareWith = ''): D throw new OCSBadRequestException('Invalid shareType'); } + // Block LinkShares if not allowed + if ($shareType === IShare::TYPE_LINK && !$this->configService->getAllowPublicLink()) { + $this->logger->debug('Link Share not allowed.'); + throw new OCSForbiddenException('Link Share not allowed.'); + } + try { $form = $this->formMapper->findById($formId); } catch (IMapperException $e) { diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php new file mode 100644 index 000000000..86d486b4d --- /dev/null +++ b/lib/Service/ConfigService.php @@ -0,0 +1,137 @@ + + * + * @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 + */ + public function getAllowPermitAll(): bool { + return json_decode($this->config->getAppValue($this->appName, Constants::CONFIG_KEY_ALLOWPERMITALL, "true")); + } + public function getAllowPublicLink(): bool { + return json_decode($this->config->getAppValue($this->appName, Constants::CONFIG_KEY_ALLOWPUBLICLINK, "true")); + } + 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_ALLOWPERMITALL => $this->getAllowPermitAll(), + Constants::CONFIG_KEY_ALLOWPUBLICLINK => $this->getAllowPublicLink(), + 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/Service/FormsService.php b/lib/Service/FormsService.php index b3084d5e4..39acebbf3 100644 --- a/lib/Service/FormsService.php +++ b/lib/Service/FormsService.php @@ -33,6 +33,7 @@ use OCA\Forms\Db\Share; use OCA\Forms\Db\ShareMapper; use OCA\Forms\Db\SubmissionMapper; + use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\IMapperException; use OCP\IGroup; @@ -66,6 +67,9 @@ class FormsService { /** @var SubmissionMapper */ private $submissionMapper; + /** @var ConfigService */ + private $configService; + /** @var IGroupManager */ private $groupManager; @@ -84,6 +88,7 @@ public function __construct(ActivityManager $activityManager, QuestionMapper $questionMapper, ShareMapper $shareMapper, SubmissionMapper $submissionMapper, + ConfigService $configService, IGroupManager $groupManager, ILogger $logger, IUserManager $userManager, @@ -94,6 +99,7 @@ public function __construct(ActivityManager $activityManager, $this->questionMapper = $questionMapper; $this->shareMapper = $shareMapper; $this->submissionMapper = $submissionMapper; + $this->configService = $configService; $this->groupManager = $groupManager; $this->logger = $logger; $this->userManager = $userManager; @@ -322,7 +328,7 @@ public function hasUserAccess(int $formId): bool { } // Now all remaining users are allowed, if permitAll is set. - if ($access['permitAllUsers']) { + if ($access['permitAllUsers'] && $this->configService->getAllowPermitAll()) { return true; } @@ -356,7 +362,9 @@ public function isSharedFormShown(int $formId): bool { } // Shown if permitall and showntoall are both set. - if ($access['permitAllUsers'] && $access['showToAllUsers']) { + if ($access['permitAllUsers'] && + $access['showToAllUsers'] && + $this->configService->getAllowPermitAll()) { return true; } 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 @@