Skip to content

Commit 2aa4137

Browse files
authored
Merge pull request #3096 from nextcloud/audit-log
emit audit log events for changes to groupfolders
2 parents daba76d + dd7b60f commit 2aa4137

File tree

6 files changed

+86
-51
lines changed

6 files changed

+86
-51
lines changed

lib/ACL/Rule.php

+24
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use OCA\GroupFolders\ACL\UserMapping\IUserMapping;
1212
use OCA\GroupFolders\ACL\UserMapping\UserMapping;
13+
use OCP\Constants;
1314
use Sabre\Xml\Reader;
1415
use Sabre\Xml\Writer;
1516
use Sabre\Xml\XmlDeserializable;
@@ -23,6 +24,14 @@ class Rule implements XmlSerializable, XmlDeserializable, \JsonSerializable {
2324
public const MAPPING_ID = '{http://nextcloud.org/ns}acl-mapping-id';
2425
public const MAPPING_DISPLAY_NAME = '{http://nextcloud.org/ns}acl-mapping-display-name';
2526

27+
public const PERMISSIONS_MAP = [
28+
'read' => Constants::PERMISSION_READ,
29+
'write' => Constants::PERMISSION_UPDATE,
30+
'create' => Constants::PERMISSION_CREATE,
31+
'delete' => Constants::PERMISSION_DELETE,
32+
'share' => Constants::PERMISSION_SHARE,
33+
];
34+
2635
private $userMapping;
2736
private $fileId;
2837

@@ -167,4 +176,19 @@ public static function defaultRule(): Rule {
167176
0
168177
);
169178
}
179+
180+
public static function formatRulePermissions(int $mask, int $permissions): string {
181+
$result = [];
182+
foreach (self::PERMISSIONS_MAP as $name => $value) {
183+
if (($mask & $value) === $value) {
184+
$type = ($permissions & $value) === $value ? '+' : '-';
185+
$result[] = $type . $name;
186+
}
187+
}
188+
return implode(', ', $result);
189+
}
190+
191+
public function formatPermissions(): string {
192+
return self::formatRulePermissions($this->mask, $this->permissions);
193+
}
170194
}

lib/Command/ACL.php

+4-23
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@
2424
use Symfony\Component\Console\Output\OutputInterface;
2525

2626
class ACL extends FolderCommand {
27-
public const PERMISSIONS_MAP = [
28-
'read' => Constants::PERMISSION_READ,
29-
'write' => Constants::PERMISSION_UPDATE,
30-
'create' => Constants::PERMISSION_CREATE,
31-
'delete' => Constants::PERMISSION_DELETE,
32-
'share' => Constants::PERMISSION_SHARE,
33-
];
34-
3527
private RuleManager $ruleManager;
3628
private ACLManagerFactory $aclManagerFactory;
3729
private IUserManager $userManager;
@@ -88,7 +80,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
8880
$path = $input->getArgument('path');
8981
$aclManager = $this->aclManagerFactory->getACLManager($user);
9082
$permissions = $aclManager->getACLPermissionsForPath($jailPath . rtrim('/' . $path, '/'));
91-
$permissionString = $this->formatRulePermissions(Constants::PERMISSION_ALL, $permissions);
83+
$permissionString = Rule::formatRulePermissions(Constants::PERMISSION_ALL, $permissions);
9284
$output->writeln($permissionString);
9385
return 0;
9486
} else {
@@ -162,7 +154,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
162154
return -3;
163155
}
164156
$name = substr($permission, 1);
165-
if (!isset(self::PERMISSIONS_MAP[$name])) {
157+
if (!isset(Rule::PERMISSIONS_MAP[$name])) {
166158
$output->writeln('<error>incorrect format for permissions2 "' . $permission . '"</error>');
167159
return -3;
168160
}
@@ -208,7 +200,7 @@ private function printPermissions(InputInterface $input, OutputInterface $output
208200
return $rule->getUserMapping()->getType() . ': ' . $rule->getUserMapping()->getId();
209201
}, $rulesForPath);
210202
$permissions = array_map(function (Rule $rule) {
211-
return $this->formatRulePermissions($rule->getMask(), $rule->getPermissions());
203+
return $rule->formatPermissions();
212204
}, $rulesForPath);
213205
$formattedPath = substr($path, $jailPathLength);
214206
return [
@@ -229,23 +221,12 @@ private function printPermissions(InputInterface $input, OutputInterface $output
229221
}
230222
}
231223

232-
private function formatRulePermissions(int $mask, int $permissions): string {
233-
$result = [];
234-
foreach (self::PERMISSIONS_MAP as $name => $value) {
235-
if (($mask & $value) === $value) {
236-
$type = ($permissions & $value) === $value ? '+' : '-';
237-
$result[] = $type . $name;
238-
}
239-
}
240-
return implode(', ', $result);
241-
}
242-
243224
private function parsePermissions(array $permissions): array {
244225
$mask = 0;
245226
$result = 0;
246227

247228
foreach ($permissions as $permission) {
248-
$permissionValue = self::PERMISSIONS_MAP[substr($permission, 1)];
229+
$permissionValue = Rule::PERMISSIONS_MAP[substr($permission, 1)];
249230
$mask |= $permissionValue;
250231
if ($permission[0] === '+') {
251232
$result |= $permissionValue;

lib/Controller/FolderController.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public function removeFolder(int $id): DataResponse {
171171
* @RequireGroupFolderAdmin
172172
*/
173173
public function setMountPoint(int $id, string $mountPoint): DataResponse {
174-
$this->manager->setMountPoint($id, trim($mountPoint));
174+
$this->manager->renameFolder($id, trim($mountPoint));
175175
return new DataResponse(['success' => true]);
176176
}
177177

lib/DAV/ACLPlugin.php

+23-9
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
use OCA\GroupFolders\Folder\FolderManager;
1515
use OCA\GroupFolders\Mount\GroupMountPoint;
1616
use OCP\Constants;
17+
use OCP\EventDispatcher\IEventDispatcher;
1718
use OCP\IUser;
1819
use OCP\IUserSession;
20+
use OCP\Log\Audit\CriticalActionPerformedEvent;
1921
use Sabre\DAV\INode;
2022
use Sabre\DAV\PropFind;
2123
use Sabre\DAV\PropPatch;
@@ -31,19 +33,14 @@ class ACLPlugin extends ServerPlugin {
3133
public const GROUP_FOLDER_ID = '{http://nextcloud.org/ns}group-folder-id';
3234

3335
private ?Server $server = null;
34-
private RuleManager $ruleManager;
35-
private FolderManager $folderManager;
36-
private IUserSession $userSession;
3736
private ?IUser $user = null;
3837

3938
public function __construct(
40-
RuleManager $ruleManager,
41-
IUserSession $userSession,
42-
FolderManager $folderManager
39+
private RuleManager $ruleManager,
40+
private IUserSession $userSession,
41+
private FolderManager $folderManager,
42+
private IEventDispatcher $eventDispatcher,
4343
) {
44-
$this->ruleManager = $ruleManager;
45-
$this->userSession = $userSession;
46-
$this->folderManager = $folderManager;
4744
}
4845

4946
private function isAdmin(string $path): bool {
@@ -196,6 +193,23 @@ public function propPatch(string $path, PropPatch $propPatch): void {
196193
);
197194
}, $rawRules);
198195

196+
$formattedRules = array_map(function (Rule $rule) {
197+
return $rule->getUserMapping()->getType() . ' ' . $rule->getUserMapping()->getDisplayName() . ': ' . $rule->formatPermissions();
198+
}, $rules);
199+
if (count($formattedRules)) {
200+
$formattedRules = implode(', ', $formattedRules);
201+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The advanced permissions for "%s" in groupfolder with id %d was set to "%s"', [
202+
$fileInfo->getInternalPath(),
203+
$mount->getFolderId(),
204+
$formattedRules,
205+
]));
206+
} else {
207+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The advanced permissions for "%s" in groupfolder with id %d was cleared', [
208+
$fileInfo->getInternalPath(),
209+
$mount->getFolderId(),
210+
]));
211+
}
212+
199213
$existingRules = array_reduce(
200214
$this->ruleManager->getAllRulesForPaths($mount->getNumericStorageId(), [$path]),
201215
function (array $rules, array $rulesForPath) {

lib/Folder/FolderManager.php

+25-13
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
use OCP\Constants;
1818
use OCP\DB\Exception;
1919
use OCP\DB\QueryBuilder\IQueryBuilder;
20+
use OCP\EventDispatcher\IEventDispatcher;
2021
use OCP\Files\Cache\ICacheEntry;
2122
use OCP\Files\IMimeTypeLoader;
2223
use OCP\Files\IRootFolder;
2324
use OCP\IDBConnection;
2425
use OCP\IGroupManager;
2526
use OCP\IUser;
2627
use OCP\IUserManager;
28+
use OCP\Log\Audit\CriticalActionPerformedEvent;
2729
use OCP\Server;
2830
use Psr\Container\ContainerExceptionInterface;
2931
use Psr\Log\LoggerInterface;
@@ -36,7 +38,8 @@ public function __construct(
3638
private IDBConnection $connection,
3739
private IGroupManager $groupManager,
3840
private IMimeTypeLoader $mimeTypeLoader,
39-
private LoggerInterface $logger
41+
private LoggerInterface $logger,
42+
private IEventDispatcher $eventDispatcher,
4043
) {
4144
}
4245

@@ -659,20 +662,11 @@ public function createFolder(string $mountPoint): int {
659662
'mount_point' => $query->createNamedParameter($mountPoint)
660663
]);
661664
$query->executeStatement();
665+
$id = $query->getLastInsertId();
662666

663-
return $query->getLastInsertId();
664-
}
667+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('A new groupfolder "%s" was created with id %d', [$mountPoint, $id]));
665668

666-
/**
667-
* @throws Exception
668-
*/
669-
public function setMountPoint(int $folderId, string $mountPoint): void {
670-
$query = $this->connection->getQueryBuilder();
671-
672-
$query->update('group_folders')
673-
->set('mount_point', $query->createNamedParameter($mountPoint))
674-
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)));
675-
$query->executeStatement();
669+
return $id;
676670
}
677671

678672
/**
@@ -694,6 +688,8 @@ public function addApplicableGroup(int $folderId, string $groupId): void {
694688
'permissions' => $query->createNamedParameter(Constants::PERMISSION_ALL)
695689
]);
696690
$query->executeStatement();
691+
692+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The group "%s" was given access to the groupfolder with id %d', [$groupId, $folderId]));
697693
}
698694

699695
/**
@@ -715,6 +711,8 @@ public function removeApplicableGroup(int $folderId, string $groupId): void {
715711
)
716712
);
717713
$query->executeStatement();
714+
715+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The group "%s" was revoked access to the groupfolder with id %d', [$groupId, $folderId]));
718716
}
719717

720718

@@ -739,6 +737,8 @@ public function setGroupPermissions(int $folderId, string $groupId, int $permiss
739737
);
740738

741739
$query->executeStatement();
740+
741+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The permissions of group "%s" to the groupfolder with id %d was set to %d', [$groupId, $folderId, $permissions]));
742742
}
743743

744744
/**
@@ -760,6 +760,9 @@ public function setManageACL(int $folderId, string $type, string $id, bool $mana
760760
->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($id)));
761761
}
762762
$query->executeStatement();
763+
764+
$action = $manageAcl ? "given" : "revoked";
765+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The %s "%s" was %s acl management rights to the groupfolder with id %d', [$type, $id, $action, $folderId]));
763766
}
764767

765768
/**
@@ -771,6 +774,8 @@ public function removeFolder(int $folderId): void {
771774
$query->delete('group_folders')
772775
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)));
773776
$query->executeStatement();
777+
778+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The groupfolder with id %d was removed', [$folderId]));
774779
}
775780

776781
/**
@@ -783,6 +788,8 @@ public function setFolderQuota(int $folderId, int $quota): void {
783788
->set('quota', $query->createNamedParameter($quota))
784789
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)));
785790
$query->executeStatement();
791+
792+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The quota for groupfolder with id %d was set to %d bytes', [$folderId, $quota]));
786793
}
787794

788795
/**
@@ -795,6 +802,8 @@ public function renameFolder(int $folderId, string $newMountPoint): void {
795802
->set('mount_point', $query->createNamedParameter($newMountPoint))
796803
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)));
797804
$query->executeStatement();
805+
806+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('The groupfolder with id %d was renamed to "%s"', [$folderId, $newMountPoint]));
798807
}
799808

800809
/**
@@ -855,6 +864,9 @@ public function setFolderACL(int $folderId, bool $acl): void {
855864
->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId)));
856865
$query->executeStatement();
857866
}
867+
868+
$action = $acl ? "enabled" : "disabled";
869+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('Advanced permissions for the groupfolder with id %d was %s', [$folderId, $action]));
858870
}
859871

860872
/**

tests/Folder/FolderManagerTest.php

+9-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use OCA\GroupFolders\Folder\FolderManager;
1010
use OCP\Constants;
11+
use OCP\EventDispatcher\IEventDispatcher;
1112
use OCP\Files\IMimeTypeLoader;
1213
use OCP\IDBConnection;
1314
use OCP\IGroupManager;
@@ -23,18 +24,21 @@ class FolderManagerTest extends TestCase {
2324
private IGroupManager $groupManager;
2425
private IMimeTypeLoader $mimeLoader;
2526
private LoggerInterface $logger;
27+
private IEventDispatcher $eventDispatcher;
2628

2729
protected function setUp(): void {
2830
parent::setUp();
2931

3032
$this->groupManager = $this->createMock(IGroupManager::class);
3133
$this->mimeLoader = $this->createMock(IMimeTypeLoader::class);
3234
$this->logger = $this->createMock(LoggerInterface::class);
35+
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
3336
$this->manager = new FolderManager(
3437
\OC::$server->getDatabaseConnection(),
3538
$this->groupManager,
3639
$this->mimeLoader,
37-
$this->logger
40+
$this->logger,
41+
$this->eventDispatcher,
3842
);
3943
$this->clean();
4044
}
@@ -86,7 +90,7 @@ public function testSetMountpoint() {
8690
$folderId1 = $this->manager->createFolder('foo');
8791
$this->manager->createFolder('bar');
8892

89-
$this->manager->setMountPoint($folderId1, 'foo2');
93+
$this->manager->renameFolder($folderId1, 'foo2');
9094

9195
$this->assertHasFolders([
9296
['mount_point' => 'foo2', 'groups' => []],
@@ -304,7 +308,7 @@ public function testGetFoldersForUserSimple() {
304308
$db = $this->createMock(IDBConnection::class);
305309
/** @var FolderManager|\PHPUnit_Framework_MockObject_MockObject $manager */
306310
$manager = $this->getMockBuilder(FolderManager::class)
307-
->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger])
311+
->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher])
308312
->setMethods(['getFoldersForGroups'])
309313
->getMock();
310314

@@ -327,7 +331,7 @@ public function testGetFoldersForUserMerge() {
327331
$db = $this->createMock(IDBConnection::class);
328332
/** @var FolderManager|\PHPUnit_Framework_MockObject_MockObject $manager */
329333
$manager = $this->getMockBuilder(FolderManager::class)
330-
->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger])
334+
->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher])
331335
->setMethods(['getFoldersForGroups'])
332336
->getMock();
333337

@@ -363,7 +367,7 @@ public function testGetFolderPermissionsForUserMerge() {
363367
$db = $this->createMock(IDBConnection::class);
364368
/** @var FolderManager|\PHPUnit_Framework_MockObject_MockObject $manager */
365369
$manager = $this->getMockBuilder(FolderManager::class)
366-
->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger])
370+
->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher])
367371
->setMethods(['getFoldersForGroups'])
368372
->getMock();
369373

0 commit comments

Comments
 (0)