From 531815e06911fadc618bf4464f9bd717f1198923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Wed, 3 Jan 2024 14:00:19 +0100 Subject: [PATCH] feat: Implement handling for lock managers who can unlock files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- lib/AppInfo/Application.php | 1 + lib/DAV/LockPlugin.php | 4 +++ lib/Service/LockService.php | 63 +++++++++++++++++++------------------ src/helper.ts | 7 +++++ src/init.ts | 1 + src/types.ts | 1 + 6 files changed, 47 insertions(+), 30 deletions(-) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index d47766b1..d6fcec94 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -67,6 +67,7 @@ class Application extends App implements IBootstrap { public const DAV_PROPERTY_LOCK_TIME = '{http://nextcloud.org/ns}lock-time'; public const DAV_PROPERTY_LOCK_TIMEOUT = '{http://nextcloud.org/ns}lock-timeout'; public const DAV_PROPERTY_LOCK_TOKEN = '{http://nextcloud.org/ns}lock-token'; + public const DAV_PROPERTY_LOCK_MANAGER = '{http://nextcloud.org/ns}lock-manager'; /** @var IUserSession */ diff --git a/lib/DAV/LockPlugin.php b/lib/DAV/LockPlugin.php index 37caeac9..a4ff2102 100644 --- a/lib/DAV/LockPlugin.php +++ b/lib/DAV/LockPlugin.php @@ -179,6 +179,10 @@ public function customProperties(PropFind $propFind, INode $node) { return $lock->getToken(); }); + + $propFind->handle(Application::DAV_PROPERTY_LOCK_MANAGER, function () use ($node) { + return $this->lockService->isLockManager($this->userSession->getUser(), $node->getNode()); + }); } public function httpLock(RequestInterface $request, ResponseInterface $response) { diff --git a/lib/Service/LockService.php b/lib/Service/LockService.php index 6540e24a..5eb41b78 100644 --- a/lib/Service/LockService.php +++ b/lib/Service/LockService.php @@ -38,14 +38,18 @@ use OCA\FilesLock\Model\FileLock; use OCA\FilesLock\Tools\Traits\TLogger; use OCA\FilesLock\Tools\Traits\TStringTools; +use OCA\GroupFolders\Mount\GroupMountPoint; use OCP\App\IAppManager; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\InvalidPathException; use OCP\Files\Lock\ILock; use OCP\Files\Lock\LockContext; use OCP\Files\Lock\OwnerLockedException; +use OCP\Files\Node; use OCP\Files\NotFoundException; +use OCP\IGroupManager; use OCP\IL10N; +use OCP\IUser; use OCP\IUserManager; /** @@ -56,47 +60,26 @@ class LockService { public const PREFIX = 'files_lock'; - use TStringTools; use TLogger; - - private ?string $userId; - private IUserManager $userManager; - private IL10N $l10n; - private LocksRequest $locksRequest; - private FileService $fileService; - private ConfigService $configService; - private IAppManager $appManager; - private IEventDispatcher $eventDispatcher; - - private array $locks = []; private bool $lockRetrieved = false; private array $lockCache = []; private ?array $directEditors = null; private bool $allowUserOverride = false; - public function __construct( - $userId, - IL10N $l10n, - IUserManager $userManager, - LocksRequest $locksRequest, - FileService $fileService, - ConfigService $configService, - IAppManager $appManager, - IEventDispatcher $eventDispatcher + private ?string $userId, + private IL10N $l10n, + private IUserManager $userManager, + private IGroupManager $groupManager, + private LocksRequest $locksRequest, + private FileService $fileService, + private ConfigService $configService, + private IAppManager $appManager, + private IEventDispatcher $eventDispatcher ) { - $this->userId = $userId; - $this->l10n = $l10n; - $this->userManager = $userManager; - $this->locksRequest = $locksRequest; - $this->fileService = $fileService; - $this->configService = $configService; - $this->appManager = $appManager; - $this->eventDispatcher = $eventDispatcher; - $this->setup('app', 'files_lock'); } @@ -402,4 +385,24 @@ private function propagateEtag(LockContext $lockContext): void { ]); $node->getStorage()->getUpdater()->propagate($node->getInternalPath(), $node->getMTime()); } + + public function isLockManager(IUser $user, ?Node $node): bool { + if ($this->groupManager->isAdmin($user->getUID())) { + return true; + } + + $lockManagerGroups = explode('|', $this->configService->getAppValue('lock_managers') ?? ''); + $userGroups = $this->groupManager->getUserGroupIds($user); + $matchGroups = !empty(array_intersect($lockManagerGroups, $userGroups)); + + if ($matchGroups) { + return true; + } + + if ($node && $node->getOwner()?->getUID() === $user?->getUID() && !$node->getMountPoint() instanceof GroupMountPoint) { + return true; + } + + return false; + } } diff --git a/src/helper.ts b/src/helper.ts index a99787e8..322dc1a9 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -33,6 +33,7 @@ export const getLockStateFromAttributes = (node: Node): LockState => { lockOwnerType: parseInt(node.attributes['lock-owner-type']), lockOwnerEditor: node.attributes['lock-owner-editor'], lockTime: parseInt(node.attributes['lock-time']), + lockManager: !!node.attributes['lock-manager'], } } @@ -53,6 +54,12 @@ export const canUnlock = (node: Node): boolean => { return false } + const aclManager = !!node.attributes['acl-can-manage'] + + if (state.lockManager || aclManager) { + return true + } + if (state.lockOwnerType === LockType.User && state.lockOwner === getCurrentUser()?.uid) { return true } diff --git a/src/init.ts b/src/init.ts index 1f796685..f03246e2 100644 --- a/src/init.ts +++ b/src/init.ts @@ -11,3 +11,4 @@ registerDavProperty('nc:lock-owner-displayname', { nc: 'http://nextcloud.org/ns' registerDavProperty('nc:lock-owner-type', { nc: 'http://nextcloud.org/ns' }) registerDavProperty('nc:lock-owner-editor', { nc: 'http://nextcloud.org/ns' }) registerDavProperty('nc:lock-time', { nc: 'http://nextcloud.org/ns' }) +registerDavProperty('nc:lock-manager', { nc: 'http://nextcloud.org/ns' }) diff --git a/src/types.ts b/src/types.ts index 1402ade0..cb461fe9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -30,5 +30,6 @@ export type LockState = { lockOwnerDisplayName: string, lockOwnerType: LockType, lockOwnerEditor: string, + lockManager: boolean, lockTime: number, }