diff --git a/appinfo/info.xml b/appinfo/info.xml index 9a0953ae..0bc1ab56 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -8,9 +8,9 @@ ## How it works The administrator can create and manage a set of rule groups. Each of the rule groups consists of one or more rules. If all rules of a group hold true, the group matches the request and access is being denied or the upload is blocked. The rules criteria range from IP address, mimetype and request time to group membership, tags, user agent and more. - + An example would be to deny access to MS Excel/XLSX files owned by the "Human Resources" group accessed from an IP not on the internal company network or to block uploads of files bigger than 512 mb by students in the "1st year" group. - + Learn more about File Access Control on [https://nextcloud.com/workflow](https://nextcloud.com/workflow) 1.8.0 @@ -36,9 +36,4 @@ Learn more about File Access Control on [https://nextcloud.com/workflow](https:/ - - - OCA\FilesAccessControl\Settings\Admin - OCA\FilesAccessControl\Settings\Section - diff --git a/js/admin.js b/js/admin.js index d0357e9e..0ea19b3c 100644 --- a/js/admin.js +++ b/js/admin.js @@ -19,77 +19,9 @@ */ (function() { - OCA.FilesAccessControl = OCA.FilesAccessControl || {}; - - /** - * @class OCA.FilesAccessControl.Operation - */ - OCA.FilesAccessControl.Operation = - OCA.WorkflowEngine.Operation.extend({ - defaults: { - 'class': 'OCA\\FilesAccessControl\\Operation', - 'name': '', - 'checks': [], - 'operation': 'deny' - } - }); - - /** - * @class OCA.FilesAccessControl.OperationsCollection - * - * collection for all configurated operations - */ - OCA.FilesAccessControl.OperationsCollection = - OCA.WorkflowEngine.OperationsCollection.extend({ - model: OCA.FilesAccessControl.Operation - }); - - /** - * @class OCA.FilesAccessControl.OperationView - * - * this creates the view for a single operation - */ - OCA.FilesAccessControl.OperationView = - OCA.WorkflowEngine.OperationView.extend({ - model: OCA.FilesAccessControl.Operation, - render: function() { - var $el = OCA.WorkflowEngine.OperationView.prototype.render.apply(this); - $el.find('input.operation-operation').addClass('hidden'); - } - }); - - /** - * @class OCA.FilesAccessControl.OperationsView - * - * this creates the view for configured operations - */ - OCA.FilesAccessControl.OperationsView = - OCA.WorkflowEngine.OperationsView.extend({ - initialize: function() { - OCA.WorkflowEngine.OperationsView.prototype.initialize.apply(this, [ - 'OCA\\FilesAccessControl\\Operation' - ]); - }, - renderOperation: function(operation) { - var subView = new OCA.FilesAccessControl.OperationView({ - model: operation - }); - - OCA.WorkflowEngine.OperationsView.prototype.renderOperation.apply(this, [ - subView - ]); - } - }); + OCA.WorkflowEngine.registerOperator({ + id: 'OCA\\FilesAccessControl\\Operation', + color: '#ca2b2b', + operation: 'deny', + }) })(); - - -$(document).ready(function() { - OC.SystemTags.collection.fetch({ - success: function() { - new OCA.FilesAccessControl.OperationsView({ - el: '#files_accesscontrol .rules', - collection: new OCA.FilesAccessControl.OperationsCollection() - }); - } - }); -}); diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f5d48df0..02f551c3 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -22,9 +22,13 @@ namespace OCA\FilesAccessControl\AppInfo; use OC\Files\Filesystem; +use OCA\FilesAccessControl\Operation; use OCA\FilesAccessControl\StorageWrapper; +use OCA\WorkflowEngine\Manager; use OCP\Files\Storage\IStorage; use OCP\Util; +use OCP\WorkflowEngine\IManager; +use Symfony\Component\EventDispatcher\GenericEvent; class Application extends \OCP\AppFramework\App { @@ -36,7 +40,14 @@ public function __construct() { * Register all hooks and listeners */ public function registerHooksAndListeners() { + $container = $this->getContainer(); Util::connectHook('OC_Filesystem', 'preSetup', $this, 'addStorageWrapper'); + $container->getServer()->getEventDispatcher()->addListener(IManager::EVENT_NAME_REG_OPERATION, function (GenericEvent $event) use ($container){ + $operation = $container->query(Operation::class); + $event->getSubject()->registerOperation($operation); + Util::addScript('files_accesscontrol', 'admin'); + }); + } /** diff --git a/lib/Operation.php b/lib/Operation.php index 94306d38..816be49b 100644 --- a/lib/Operation.php +++ b/lib/Operation.php @@ -22,19 +22,27 @@ namespace OCA\FilesAccessControl; +use OCA\WorkflowEngine\Entity\File; +use OCP\EventDispatcher\Event; use OCP\Files\ForbiddenException; use OCP\Files\Storage\IStorage; use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\WorkflowEngine\IComplexOperation; use OCP\WorkflowEngine\IManager; -use OCP\WorkflowEngine\IOperation; +use OCP\WorkflowEngine\IRuleMatcher; +use OCP\WorkflowEngine\ISpecificOperation; -class Operation implements IOperation{ +class Operation implements IComplexOperation, ISpecificOperation { /** @var IManager */ protected $manager; /** @var IL10N */ protected $l; + /** @var IURLGenerator */ + protected $urlGenerator; + /** @var int */ protected $nestingLevel = 0; @@ -42,9 +50,10 @@ class Operation implements IOperation{ * @param IManager $manager * @param IL10N $l */ - public function __construct(IManager $manager, IL10N $l) { + public function __construct(IManager $manager, IL10N $l, IURLGenerator $urlGenerator) { $this->manager = $manager; $this->l = $l; + $this->urlGenerator = $urlGenerator; } /** @@ -63,8 +72,9 @@ public function checkFileAccess(IStorage $storage, $path) { $this->nestingLevel++; $filePath = $this->translatePath($storage, $path); - $this->manager->setFileInfo($storage, $filePath); - $match = $this->manager->getMatchingOperations('OCA\FilesAccessControl\Operation'); + $ruleMatcher = $this->manager->getRuleMatcher(); + $ruleMatcher->setFileInfo($storage, $filePath); + $match = $ruleMatcher->getMatchingOperations(self::class); $this->nestingLevel--; @@ -166,9 +176,91 @@ protected function isCreatingSkeletonFiles() { * @param string $operation * @throws \UnexpectedValueException */ - public function validateOperation($name, array $checks, $operation) { + public function validateOperation(string $name, array $checks, string $operation): void { if (empty($checks)) { throw new \UnexpectedValueException($this->l->t('No rule given')); } } + + /** + * returns a translated name to be presented in the web interface + * + * Example: "Automated tagging" (en), "AĆ­tomata etikedado" (eo) + * + * @since 18.0.0 + */ + public function getDisplayName(): string { + return $this->l->t('Block access to a file'); + } + + /** + * returns a translated, descriptive text to be presented in the web interface. + * + * It should be short and precise. + * + * Example: "Tag based automatic deletion of files after a given time." (en) + * + * @since 18.0.0 + */ + public function getDescription(): string { + return ''; + } + + /** + * returns the URL to the icon of the operator for display in the web interface. + * + * Usually, the implementation would utilize the `imagePath()` method of the + * `\OCP\IURLGenerator` instance and simply return its result. + * + * Example implementation: return $this->urlGenerator->imagePath('myApp', 'cat.svg'); + * + * @since 18.0.0 + */ + public function getIcon(): string { + return $this->urlGenerator->imagePath('files_accesscontrol', 'app.svg'); + } + + /** + * returns whether the operation can be used in the requested scope. + * + * Scope IDs are defined as constants in OCP\WorkflowEngine\IManager. At + * time of writing these are SCOPE_ADMIN and SCOPE_USER. + * + * For possibly unknown future scopes the recommended behaviour is: if + * user scope is permitted, the default behaviour should return `true`, + * otherwise `false`. + * + * @since 18.0.0 + */ + public function isAvailableForScope(int $scope): bool { + return $scope === IManager::SCOPE_ADMIN; + } + + /** + * returns the id of the entity the operator is designed for + * + * Example: 'WorkflowEngine_Entity_File' + * + * @since 18.0.0 + */ + public function getEntityId(): string { + return File::class; + } + + /** + * As IComplexOperation chooses the triggering events itself, a hint has + * to be shown to the user so make clear when this operation is becoming + * active. This method returns such a translated string. + * + * Example: "When a file is accessed" (en) + * + * @since 18.0.0 + */ + public function getTriggerHint(): string { + return $this->l->t('File is accessed'); + } + + public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatcher): void { + // Noop + } } diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php deleted file mode 100644 index f4d85819..00000000 --- a/lib/Settings/Admin.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * @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\FilesAccessControl\Settings; - -use OCA\FilesAccessControl\AppInfo\Application; -use OCP\AppFramework\Http\TemplateResponse; -use OCP\IL10N; -use OCP\Settings\ISettings; -use OCP\Util; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - -class Admin implements ISettings { - - /** @var IL10N */ - private $l10n; - - /** @var Application */ - private $app; - - /** @var EventDispatcherInterface */ - private $eventDispatcher; - - public function __construct(IL10N $l10n, Application $app, EventDispatcherInterface $eventDispatcher) { - $this->l10n = $l10n; - $this->app = $app; - $this->eventDispatcher = $eventDispatcher; - } - - /** - * @return TemplateResponse - */ - public function getForm() { - $appName = $this->app->getContainer()->getAppName(); - $this->eventDispatcher->dispatch('OCP\WorkflowEngine::loadAdditionalSettingScripts'); - Util::addScript($appName, 'admin'); - $parameters = [ - 'appid' => $appName, - 'docs' => 'admin-files-access-control', - 'heading' => $this->l10n->t('File access control'), - 'settings-hint' => $this->l10n->t('Restrict access to files based on factors such as filetype, user group memberships, time and more.'), - 'description' => $this->l10n->t('Each rule group consists of one or more rules. A request matches a group if all rules evaluate to true. If a request matches at least one of the defined groups, the request is blocked and the file content can not be read or written.'), - ]; - - return new TemplateResponse('workflowengine', 'admin', $parameters, 'blank'); - } - - /** - * @return string the section ID, e.g. 'sharing' - */ - public function getSection() { - return 'files_accesscontrol'; - } - - /** - * @return int whether the form should be rather on the top or bottom of - * the admin section. The forms are arranged in ascending order of the - * priority values. It is required to return a value between 0 and 100. - * - * E.g.: 70 - */ - public function getPriority() { - return 70; - } - -} diff --git a/lib/Settings/Section.php b/lib/Settings/Section.php deleted file mode 100644 index 853e8b88..00000000 --- a/lib/Settings/Section.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * @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\FilesAccessControl\Settings; - -use OCP\IL10N; -use OCP\IURLGenerator; -use OCP\Settings\IIconSection; - -class Section implements IIconSection { - /** @var IL10N */ - private $l; - /** @var IURLGenerator */ - private $url; - - public function __construct(IL10N $l, IURLGenerator $url) { - $this->l = $l; - $this->url = $url; - } - - /** - * returns the ID of the section. It is supposed to be a lower case string, - * e.g. 'ldap' - * - * @returns string - */ - public function getID() { - return 'files_accesscontrol'; - } - - /** - * returns the translated name as it should be displayed, e.g. 'LDAP / AD - * integration'. Use the L10N service to translate it. - * - * @return string - */ - public function getName() { - return $this->l->t('File access control'); - } - - /** - * @return int whether the form should be rather on the top or bottom of - * the settings navigation. The sections are arranged in ascending order of - * the priority values. It is required to return a value between 0 and 99. - * - * E.g.: 70 - */ - public function getPriority() { - return 70; - } - - /** - * {@inheritdoc} - */ - public function getIcon() { - return $this->url->imagePath('files_accesscontrol', 'app-dark.svg'); - } -} diff --git a/tests/Settings/AdminTest.php b/tests/Settings/AdminTest.php deleted file mode 100644 index 93f57b3f..00000000 --- a/tests/Settings/AdminTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * @author Joas Schilling g - * @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\FilesAccessControl\Settings\Test; - -use OCA\FilesAccessControl\AppInfo\Application; -use OCA\FilesAccessControl\Settings\Admin; -use OCP\AppFramework\IAppContainer; -use OCP\IL10N; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Test\TestCase; -use OCP\AppFramework\Http\TemplateResponse; - -class AdminTest extends TestCase { - /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ - protected $l; - - /** @var Application|\PHPUnit_Framework_MockObject_MockObject */ - protected $app; - - /** @var EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $dispatcher; - - /** @var Admin */ - protected $admin; - - /** @var IAppContainer|\PHPUnit_Framework_MockObject_MockObject */ - protected $container; - - protected function setUp() { - parent::setUp(); - - $this->l = $this->createMock(IL10N::class); - $this->app = $this->createMock(Application::class); - $this->dispatcher = $this->createMock(EventDispatcherInterface::class); - $this->container = $this->createMock(IAppContainer::class); - - $this->admin = new Admin($this->l, $this->app, $this->dispatcher); - } - - public function testGetForm() { - $this->dispatcher->expects($this->once()) - ->method('dispatch') - ->with('OCP\WorkflowEngine::loadAdditionalSettingScripts'); - - $this->l->expects($this->exactly(3)) - ->method('t') - ->will($this->returnCallback(function($text, $parameters = []) { - return vsprintf($text, $parameters); - })); - - $this->container->expects($this->once()) - ->method('getAppName') - ->willReturn('files_accesscontrol'); - - $this->app->expects($this->once()) - ->method('getContainer') - ->willReturn($this->container); - - $result = $this->admin->getForm(); - $this->assertInstanceOf(TemplateResponse::class, $result); - } -}