From c733a806d1b6ca4422c197c61c58a68fb7c15071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=A1vio=20Gomes=20da=20Silva=20Lisboa?= Date: Mon, 11 Dec 2017 11:03:23 -0200 Subject: [PATCH] Enable audit by configuration #163 --- appinfo/app.php | 2 +- js/admin.js | 11 +++- js/circles.app.actions.js | 3 +- js/circles.app.elements.js | 2 + js/circles.app.js | 2 + js/circles.app.settings.js | 10 +-- l10n/pt_BR.js | 4 +- l10n/pt_BR.json | 4 +- lib/Api/v1/Circles.php | 4 +- lib/AppInfo/Application.php | 21 ++++++ lib/Controller/CirclesController.php | 19 +++++- lib/Controller/MembersController.php | 9 +++ lib/Controller/SettingsController.php | 8 ++- lib/Events/UserEvents.php | 94 ++++++++++++++++++++++++++- lib/Hooks/UserHooks.php | 35 +++++++++- lib/Service/ConfigService.php | 22 ++++++- lib/ShareByCircleProvider.php | 37 ++++++++++- templates/settings.admin.php | 8 +++ 18 files changed, 276 insertions(+), 19 deletions(-) diff --git a/appinfo/app.php b/appinfo/app.php index f4bb8455f..f6590e2f8 100644 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -32,5 +32,5 @@ $app->registerNavigation(); $app->registerFilesNavigation(); $app->registerFilesPlugin(); - +$app->registerHooks(); diff --git a/js/admin.js b/js/admin.js index f4866237c..df151d80f 100644 --- a/js/admin.js +++ b/js/admin.js @@ -44,6 +44,7 @@ $(document).ready(function () { elements.test_async_result = $('#test_async_result'); elements.allow_linked_groups = $('#allow_linked_groups'); elements.allow_federated_circles = $('#allow_federated_circles'); + elements.enable_audit = $('#enable_audit'); elements.test_async_wait.hide().on('click', function () { self.refreshResult(); @@ -74,6 +75,10 @@ $(document).ready(function () { elements.allow_federated_circles.on('change', function () { saveChange(); }); + + elements.enable_audit.on('change', function () { + saveChange(); + }); saveChange = function () { $.ajax({ @@ -83,11 +88,14 @@ $(document).ready(function () { allow_linked_groups: (elements.allow_linked_groups.is( ':checked')) ? '1' : '0', allow_federated_circles: (elements.allow_federated_circles.is( - ':checked')) ? '1' : '0' + ':checked')) ? '1' : '0', + enable_audit: (elements.enable_audit.is( + ':checked')) ? '1' : '0' } }).done(function (res) { elements.allow_linked_groups.prop('checked', (res.allowLinkedGroups === '1')); elements.allow_federated_circles.prop('checked', (res.allowFederatedCircles === '1')); + elements.enable_audit.prop('checked', (res.enableAudit === '1')); }); }; @@ -170,6 +178,7 @@ $(document).ready(function () { }).done(function (res) { elements.allow_linked_groups.prop('checked', (res.allowLinkedGroups === '1')); elements.allow_federated_circles.prop('checked', (res.allowFederatedCircles === '1')); + elements.enable_audit.prop('checked', (res.enableAudit === '1')); }); var timerTestAsync = setInterval(function () { diff --git a/js/circles.app.actions.js b/js/circles.app.actions.js index cf92b2720..3cfffc9c9 100644 --- a/js/circles.app.actions.js +++ b/js/circles.app.actions.js @@ -128,7 +128,8 @@ var actions = { circle_desc: elements.settingsDesc.val(), allow_links: (elements.settingsLink.is(":checked")), allow_links_auto: (elements.settingsLinkAuto.is(":checked")), - allow_links_files: (elements.settingsLinkFiles.is(":checked")) + allow_links_files: (elements.settingsLinkFiles.is(":checked")), + enable_audit: (elements.settingsEnableAudit.is(":checked")) }; api.settingsCircle(curr.circle, data, settings.saveSettingsResult); diff --git a/js/circles.app.elements.js b/js/circles.app.elements.js index 747414e5c..6544f6d73 100644 --- a/js/circles.app.elements.js +++ b/js/circles.app.elements.js @@ -75,6 +75,7 @@ var elements = { settingsEntryLink: null, settingsEntryLinkAuto: null, settingsEntryLinkFiles: null, + settingsEnableAudit: null, settingsSave: null, addMember: null, @@ -131,6 +132,7 @@ var elements = { elements.settingsEntryLink = $('#settings-entry-link'); elements.settingsEntryLinkAuto = $('#settings-entry-link-auto'); elements.settingsEntryLinkFiles = $('#settings-entry-link-files'); + elements.settingsEnableAudit = $('#settings-enable-audit'); elements.settingsSave = $('#settings-submit'); elements.addMember = $('#addmember'); diff --git a/js/circles.app.js b/js/circles.app.js index 61ee8dcc7..95bd5f0c3 100644 --- a/js/circles.app.js +++ b/js/circles.app.js @@ -60,6 +60,7 @@ var curr = { allowed_linked_groups: 0, allowed_federated_circles: 0, allowed_circles: 0, + enabled_audit: 0, defineCircle: function (data) { curr.circle = data.circle_id; @@ -192,6 +193,7 @@ $(document).ready(function () { curr.allowed_circles = result.allowed_circles; curr.allowed_linked_groups = result.allowed_linked_groups; curr.allowed_federated_circles = result.allowed_federated_circles; + curr.enabled_audit = result.enabled_audit; var circleId = window.location.hash.substr(1); if (circleId) { diff --git a/js/circles.app.settings.js b/js/circles.app.settings.js index 7ddaf6fd9..315cfe83b 100644 --- a/js/circles.app.settings.js +++ b/js/circles.app.settings.js @@ -54,6 +54,8 @@ var settings = { (curr.circleSettings['allow_links_auto'] === 'true')); elements.settingsLinkFiles.prop('checked', (curr.circleSettings['allow_links_files'] === 'true')); + elements.settingsEnableAudit.prop('checked', + (curr.circleSettings['enable_audit'] === 'true')); elements.settingsLink.on('change', function () { settings.interactUISettings(); @@ -79,6 +81,9 @@ var settings = { (elements.settingsLink.is(":checked"))); settings.enableSetting(elements.settingsEntryLinkFiles, elements.settingsLinkFiles, (elements.settingsLink.is(":checked"))); + settings.enableSetting(elements.settingsEntryEnableAudit, elements.settingsEnableAudit, + (elements.settingsEnableAudit.is(":checked"))); + }, enableSetting: function (entry, input, enable) { @@ -100,7 +105,4 @@ var settings = { nav.displayMembersInteraction(result.details); OCA.notification.onSuccess(t('circles', "Settings saved.")); } -}; - - - +}; \ No newline at end of file diff --git a/l10n/pt_BR.js b/l10n/pt_BR.js index d94f9f8a3..ce18202ac 100644 --- a/l10n/pt_BR.js +++ b/l10n/pt_BR.js @@ -274,6 +274,8 @@ OC.L10N.register( "Allow linking of groups:" : "Permitir links dos grupos:", "Groups can be linked to circles." : "Grupos podem ser linkados a círculos.", "Allow federated circles:" : "Permitir círculos federados:", - "Circles from different Nextclouds can be linked together." : "Círculos de diferentes Nextclouds podem ser linkados juntos." + "Circles from different Nextclouds can be linked together." : "Círculos de diferentes Nextclouds podem ser linkados juntos.", + "Enable audit:" : "Habilitar auditoria:", + "Actions of circles, members and sharing can be audited." : "Ações de círculos, membros e compartilhamentos podem ser auditadas." }, "nplurals=2; plural=(n > 1);"); diff --git a/l10n/pt_BR.json b/l10n/pt_BR.json index fd542a4c4..6f6278212 100644 --- a/l10n/pt_BR.json +++ b/l10n/pt_BR.json @@ -272,6 +272,8 @@ "Allow linking of groups:" : "Permitir links dos grupos:", "Groups can be linked to circles." : "Grupos podem ser linkados a círculos.", "Allow federated circles:" : "Permitir círculos federados:", - "Circles from different Nextclouds can be linked together." : "Círculos de diferentes Nextclouds podem ser linkados juntos." + "Circles from different Nextclouds can be linked together." : "Círculos de diferentes Nextclouds podem ser linkados juntos.", + "Enable audit:" : "Habilitar auditoria:", + "Actions of circles, members and sharing can be audited." : "Ações de círculos, membros e compartilhamentos podem ser auditadas." },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/lib/Api/v1/Circles.php b/lib/Api/v1/Circles.php index 104af315b..c3c48504b 100644 --- a/lib/Api/v1/Circles.php +++ b/lib/Api/v1/Circles.php @@ -381,8 +381,10 @@ public static function shareToCircle( $frame = new SharingFrame((string)$source, (string)$type); $frame->setPayload($payload); - return $c->query(SharingFrameService::class) + $result = $c->query(SharingFrameService::class) ->createFrame($circleUniqueId, $frame, (string)$broadcaster); + + return $result; } diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index a0c958215..3a9ae9801 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -70,6 +70,27 @@ public function registerHooks() { Util::connectHook( 'OC_User', 'post_deleteGroup', '\OCA\Circles\Hooks\UserHooks', 'onGroupDeleted' ); + Util::connectHook( + 'OCA\Circles', 'post_createCircle', '\OCA\Circles\Hooks\UserHooks', 'onCircleCreated' + ); + Util::connectHook( + 'OCA\Circles', 'post_destroyCircle', '\OCA\Circles\Hooks\UserHooks', 'onCircleDestroyed' + ); + Util::connectHook( + 'OCA\Circles', 'post_updateCircle', '\OCA\Circles\Hooks\UserHooks', 'onCircleUpdated' + ); + Util::connectHook( + 'OCA\Circles', 'post_addMember', '\OCA\Circles\Hooks\UserHooks', 'onMemberAdded' + ); + Util::connectHook( + 'OCA\Circles', 'post_removeMember', '\OCA\Circles\Hooks\UserHooks', 'onMemberRemoved' + ); + Util::connectHook( + 'OCP\Share', 'post_share', '\OCA\Circles\Hooks\UserHooks', 'onItemShared' + ); + Util::connectHook( + 'OCP\Share', 'post_unshare', '\OCA\Circles\Hooks\UserHooks', 'onItemUnshared' + ); } diff --git a/lib/Controller/CirclesController.php b/lib/Controller/CirclesController.php index 1ea2e3d2e..e961f8a37 100644 --- a/lib/Controller/CirclesController.php +++ b/lib/Controller/CirclesController.php @@ -32,6 +32,8 @@ use OCA\Circles\Exceptions\CircleTypeDisabledException; use OCA\Circles\Exceptions\FederatedCircleNotAllowedException; use OCP\AppFramework\Http\DataResponse; +use OCA\Circles\Model\Circle; +use OCP\Util; class CirclesController extends BaseController { @@ -51,7 +53,9 @@ public function create($type, $name) { try { $this->verifyCreationName($name); $data = $this->circlesService->createCircle($type, $name); - + if ($this->configService->isAuditEnabled()){ + Util::emitHook('OCA\Circles', 'post_createCircle', ['circle' => $name]); + } return $this->success(['name' => $name, 'circle' => $data, 'type' => $type]); } catch (Exception $e) { return $this->fail(['type' => $type, 'name' => $name, 'error' => $e->getMessage()]); @@ -116,8 +120,13 @@ public function details($uniqueId) { public function settings($uniqueId, $settings) { try { $this->verifyCreationName($settings['circle_name']); + $formerCircle = $this->details($uniqueId)->getData()['details']->getName(); $circle = $this->circlesService->settingsCircle($uniqueId, $settings); - + if ($this->configService->isAuditEnabled()){ + $settings['former_name']= $formerCircle; + Util::emitHook('OCA\Circles', 'post_updateCircle', $settings); + } + return $this->success(['circle_id' => $uniqueId, 'details' => $circle]); } catch (\Exception $e) { @@ -178,8 +187,12 @@ public function leave($uniqueId) { */ public function destroy($uniqueId) { try { + $circle = $this->details($uniqueId)->getData()['details']->getName(); $this->circlesService->removeCircle($uniqueId); - + if ($this->configService->isAuditEnabled()){ + Util::emitHook('OCA\Circles', 'post_destroyCircle', ['circle' => $circle]); + } + return $this->success(['circle_id' => $uniqueId]); } catch (\Exception $e) { return $this->fail(['circle_id' => $uniqueId, 'error' => $e->getMessage()]); diff --git a/lib/Controller/MembersController.php b/lib/Controller/MembersController.php index eefa132ea..27bfd4e31 100644 --- a/lib/Controller/MembersController.php +++ b/lib/Controller/MembersController.php @@ -29,6 +29,7 @@ use OCA\Circles\Model\Member; use OCA\Circles\Service\MiscService; use OCP\AppFramework\Http\DataResponse; +use OCP\Util; class MembersController extends BaseController { @@ -47,6 +48,10 @@ public function addMember($uniqueId, $ident, $type) { try { $data = $this->membersService->addMember($uniqueId, $ident, (int)$type); + if ($this->configService->isAuditEnabled()){ + $circle = $this->circlesService->detailsCircle($uniqueId); + Util::emitHook('OCA\Circles', 'post_addMember', ['circle' => $circle->getName(), 'member' => $ident]); + } } catch (\Exception $e) { return $this->fail( [ @@ -127,6 +132,10 @@ public function removeMember($uniqueId, $member, $type) { try { $data = $this->membersService->removeMember($uniqueId, $member, (int)$type); + if ($this->configService->isAuditEnabled()){ + $circle = $this->circlesService->detailsCircle($uniqueId); + Util::emitHook('OCA\Circles', 'post_removeMember', ['circle' => $circle->getName(), 'member' => $member]); + } } catch (\Exception $e) { return $this->fail( diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index a5fd2f446..bbabb28fc 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -46,6 +46,9 @@ public function getSettings() { ), 'allowFederatedCircles' => $this->configService->getAppValue( ConfigService::CIRCLES_ALLOW_FEDERATED_CIRCLES + ), + 'enableAudit' => $this->configService->getAppValue( + ConfigService::CIRCLES_ENABLE_AUDIT ) ]; @@ -53,13 +56,16 @@ public function getSettings() { } - public function setSettings($allow_linked_groups, $allow_federated_circles) { + public function setSettings($allow_linked_groups, $allow_federated_circles, $enable_audit) { $this->configService->setAppValue( ConfigService::CIRCLES_ALLOW_LINKED_GROUPS, $allow_linked_groups ); $this->configService->setAppValue( ConfigService::CIRCLES_ALLOW_FEDERATED_CIRCLES, $allow_federated_circles ); + $this->configService->setAppValue( + ConfigService::CIRCLES_ENABLE_AUDIT, $enable_audit + ); return $this->getSettings(); } diff --git a/lib/Events/UserEvents.php b/lib/Events/UserEvents.php index 8b8986a94..ee6a217c7 100644 --- a/lib/Events/UserEvents.php +++ b/lib/Events/UserEvents.php @@ -8,8 +8,15 @@ use OCA\Circles\Service\GroupsService; use OCA\Circles\Service\MembersService; use OCA\Circles\Service\MiscService; +use OCP\Util; +use OC\User\User; +use OC\Log; +use OCA\Circles\AppInfo\Application; class UserEvents { + + /** Default for warning log level **/ + const logLevel = 2; /** @var CirclesService */ private $circlesService; @@ -22,6 +29,9 @@ class UserEvents { /** @var MiscService */ private $miscService; + + /** @var User */ + private static $user = null; /** * UserEvents constructor. @@ -59,6 +69,88 @@ public function onGroupDeleted(array $params) { $groupId = $params['gid']; $this->groupsService->onGroupRemoved($groupId); } + + /** + * @param array $params + */ + public function onCircleCreated(array $params) { + $circle = $params['circle']; + $user = $this->getUser()->getDisplayName(); + $this->miscService->log("user $user created circle $circle", self::logLevel); + } + + /** + * @param array $params + */ + public function onCircleDestroyed(array $params) { + $circle = $params['circle']; + $user = $this->getUser()->getDisplayName(); + $this->miscService->log("user $user destroyed circle $circle", self::logLevel); + } + + /** + * @param array $params + */ + public function onCircleUpdated(array $params) { + $formerCircle = $params['former_name']; + $circle = $params['circle_name']; + if ($formerCircle != $circle){ + $user = $this->getUser()->getDisplayName(); + $this->miscService->log("user $user updated circle $formerCircle to $circle", self::logLevel); + } + } + + /** + * @param array $params + */ + public function onMemberAdded(array $params) { + $circle = $params['circle']; + $member = $params['member']; + $user = $this->getUser()->getDisplayName(); + $this->miscService->log("user $user added member $member to circle $circle", self::logLevel); + } + + /** + * @param array $params + */ + public function onMemberRemoved(array $params) { + $circle = $params['circle']; + $member = $params['member']; + $user = $this->getUser()->getDisplayName(); + $this->miscService->log("user $user removed member $member from circle $circle", self::logLevel); + } + + /** + * @param array $params + */ + public function onItemShared(array $params) { + $shareWith = $params['shareWith']; + $fileTarget = $params['fileTarget']; + $user = $this->getUser()->getDisplayName(); + $this->miscService->log("user $user shared $fileTarget with $shareWith", self::logLevel); + } -} + /** + * @param array $params + */ + public function onItemUnshared(array $params) { + $shareWith = $params['shareWith']; + $fileTarget = $params['fileTarget']; + $user = $this->getUser()->getDisplayName(); + if (!empty($shareWith)){ + $this->miscService->log("user $user unshared $fileTarget with $shareWith", self::logLevel); + } + } + /** + * @return User + */ + private function getUser() + { + if (self::$user == null){ + $app = new Application(); + self::$user = $app->getContainer()->query('UserSession')->getUser(); + } + return self::$user; + } +} \ No newline at end of file diff --git a/lib/Hooks/UserHooks.php b/lib/Hooks/UserHooks.php index eb8e7e6fe..c7dfd8177 100644 --- a/lib/Hooks/UserHooks.php +++ b/lib/Hooks/UserHooks.php @@ -28,5 +28,38 @@ public static function onGroupDeleted($params) { ->onGroupDeleted($params); } -} + public static function onCircleCreated($params) { + self::getController() + ->onCircleCreated($params); + } + + public static function onCircleDestroyed($params) { + self::getController() + ->onCircleDestroyed($params); + } + public static function onCircleUpdated($params) { + self::getController() + ->onCircleUpdated($params); + } + + public static function onMemberAdded($params) { + self::getController() + ->onMemberAdded($params); + } + + public static function onMemberRemoved($params) { + self::getController() + ->onMemberRemoved($params); + } + + public static function onItemShared($params) { + self::getController() + ->onItemShared($params); + } + + public static function onItemUnshared($params) { + self::getController() + ->onItemUnshared($params); + } +} \ No newline at end of file diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php index 347b745e6..fc7ce1ba7 100644 --- a/lib/Service/ConfigService.php +++ b/lib/Service/ConfigService.php @@ -44,6 +44,8 @@ class ConfigService { const CIRCLES_TEST_ASYNC_INIT = 'test_async_init'; const CIRCLES_TEST_ASYNC_HAND = 'test_async_hand'; const CIRCLES_TEST_ASYNC_COUNT = 'test_async_count'; + + const CIRCLES_ENABLE_AUDIT = 'circles_enable_audit'; private $defaults = [ self::CIRCLES_ALLOW_CIRCLES => Circle::CIRCLES_ALL, @@ -52,7 +54,8 @@ class ConfigService { self::CIRCLES_ALLOW_LINKED_GROUPS => '0', self::CIRCLES_ALLOW_FEDERATED_CIRCLES => '0', self::CIRCLES_ALLOW_NON_SSL_LINKS => '0', - self::CIRCLES_NON_SSL_LOCAL => '0' + self::CIRCLES_NON_SSL_LOCAL => '0', + self::CIRCLES_ENABLE_AUDIT => '0' ]; /** @var string */ @@ -84,6 +87,9 @@ class ConfigService { /** @var int */ private $localNonSSL = -1; + + /** $var int */ + private $enableAudit = -1; /** * ConfigService constructor. @@ -300,4 +306,16 @@ public function getCloudVersion($complete = false) { return $ver[0]; } -} + + /** + * returns if audit is enabled by the current configuration. + * + * @return int + */ + public function isAuditEnabled() { + if ($this->enableAudit === -1) { + $this->enableAudit = (int)$this->getAppValue(self::CIRCLES_ENABLE_AUDIT); + } + return ((int)$this->enableAudit); + } +} \ No newline at end of file diff --git a/lib/ShareByCircleProvider.php b/lib/ShareByCircleProvider.php index 041e73d3f..11cbcca40 100644 --- a/lib/ShareByCircleProvider.php +++ b/lib/ShareByCircleProvider.php @@ -57,6 +57,7 @@ use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IShare; use OCP\Share\IShareProvider; +use OCP\Util; class ShareByCircleProvider extends CircleProviderRequest implements IShareProvider { @@ -134,6 +135,8 @@ public function identifier() { * @throws \Exception */ public function create(IShare $share) { + $circle = null; + $shareId = null; try { $nodeId = $share->getNode() ->getId(); @@ -161,8 +164,15 @@ public function create(IShare $share) { '\OCA\Circles\Circles\FileSharingBroadcaster' ); + if ($this->configService->isAuditEnabled()){ + Util::emitHook('OCP\Share', 'post_share',['shareWith'=> $circle->getName(),'fileTarget'=> $share->getTarget()]); + } + return $this->getShareById($shareId); } catch (\Exception $e) { + if ($this->getShareById($shareId) && $this->configService->isAuditEnabled()){ + Util::emitHook('OCP\Share', 'post_share',['shareWith'=> $circle->getName(),'fileTarget'=> $share->getTarget()]); + } throw $e; } } @@ -200,6 +210,18 @@ public function delete(IShare $share) { $this->limitToShareAndChildren($qb, $share->getId()); $qb->execute(); + + $app = new Application(); + $container = $app->getContainer(); + $circle = $container->query(CirclesService::class)->detailsCircle($share->getSharedWith()); + + if ($this->configService->isAuditEnabled()){ + Util::emitHook('OCP\Share', 'post_unshare',[ + 'fileTarget'=> $share->getTarget(), + 'shareType' => $share->getShareType(), + 'shareWith'=> $circle->getName() + ]); + } } @@ -218,8 +240,21 @@ public function deleteFromSelf(IShare $share, $userId) { $this->limitToShare($qb, $childId); $qb->execute(); - } + try { + $shareWith = $this->circlesRequest->getCircle($share->getSharedWith(),$userId)->getName(); + } catch (\Exception $e) { + $shareWith = $share->getSharedWith(); + } + + if ($this->configService->isAuditEnabled()){ + Util::emitHook('OCP\Share', 'post_unshare',[ + 'fileTarget'=> $share->getTarget(), + 'shareType' => $share->getShareType(), + 'shareWith'=> $shareWith + ]); + } + } /** * Move a share as a recipient. diff --git a/templates/settings.admin.php b/templates/settings.admin.php index 38e6a9193..f9b9741dc 100644 --- a/templates/settings.admin.php +++ b/templates/settings.admin.php @@ -36,5 +36,13 @@ + + t('Enable audit:')); ?>
+ t('Actions of circles, members and sharing can be audited.')); ?> + + + + +