Skip to content

Commit 40f1ad6

Browse files
committed
Add public API for owner based locking
Signed-off-by: Julius Härtl <[email protected]>
1 parent 94004a7 commit 40f1ad6

11 files changed

Lines changed: 574 additions & 0 deletions

File tree

lib/composer/composer/autoload_classmap.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,12 @@
295295
'OCP\\Files\\InvalidDirectoryException' => $baseDir . '/lib/public/Files/InvalidDirectoryException.php',
296296
'OCP\\Files\\InvalidPathException' => $baseDir . '/lib/public/Files/InvalidPathException.php',
297297
'OCP\\Files\\LockNotAcquiredException' => $baseDir . '/lib/public/Files/LockNotAcquiredException.php',
298+
'OCP\\Files\\Lock\\ILock' => $baseDir . '/lib/public/Files/Lock/ILock.php',
299+
'OCP\\Files\\Lock\\ILockManager' => $baseDir . '/lib/public/Files/Lock/ILockManager.php',
300+
'OCP\\Files\\Lock\\ILockProvider' => $baseDir . '/lib/public/Files/Lock/ILockProvider.php',
301+
'OCP\\Files\\Lock\\LockScope' => $baseDir . '/lib/public/Files/Lock/LockScope.php',
302+
'OCP\\Files\\Lock\\NoLockProviderException' => $baseDir . '/lib/public/Files/Lock/NoLockProviderException.php',
303+
'OCP\\Files\\Lock\\OwnerLockedException' => $baseDir . '/lib/public/Files/Lock/OwnerLockedException.php',
298304
'OCP\\Files\\Mount\\IMountManager' => $baseDir . '/lib/public/Files/Mount/IMountManager.php',
299305
'OCP\\Files\\Mount\\IMountPoint' => $baseDir . '/lib/public/Files/Mount/IMountPoint.php',
300306
'OCP\\Files\\Node' => $baseDir . '/lib/public/Files/Node.php',
@@ -1129,6 +1135,7 @@
11291135
'OC\\Files\\Config\\UserMountCacheListener' => $baseDir . '/lib/private/Files/Config/UserMountCacheListener.php',
11301136
'OC\\Files\\FileInfo' => $baseDir . '/lib/private/Files/FileInfo.php',
11311137
'OC\\Files\\Filesystem' => $baseDir . '/lib/private/Files/Filesystem.php',
1138+
'OC\\Files\\Lock\\LockManager' => $baseDir . '/lib/private/Files/Lock/LockManager.php',
11321139
'OC\\Files\\Mount\\CacheMountProvider' => $baseDir . '/lib/private/Files/Mount/CacheMountProvider.php',
11331140
'OC\\Files\\Mount\\LocalHomeMountProvider' => $baseDir . '/lib/private/Files/Mount/LocalHomeMountProvider.php',
11341141
'OC\\Files\\Mount\\Manager' => $baseDir . '/lib/private/Files/Mount/Manager.php',

lib/composer/composer/autoload_static.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
324324
'OCP\\Files\\InvalidDirectoryException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidDirectoryException.php',
325325
'OCP\\Files\\InvalidPathException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidPathException.php',
326326
'OCP\\Files\\LockNotAcquiredException' => __DIR__ . '/../../..' . '/lib/public/Files/LockNotAcquiredException.php',
327+
'OCP\\Files\\Lock\\ILock' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/ILock.php',
328+
'OCP\\Files\\Lock\\ILockManager' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/ILockManager.php',
329+
'OCP\\Files\\Lock\\ILockProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/ILockProvider.php',
330+
'OCP\\Files\\Lock\\LockScope' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/LockScope.php',
331+
'OCP\\Files\\Lock\\NoLockProviderException' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/NoLockProviderException.php',
332+
'OCP\\Files\\Lock\\OwnerLockedException' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/OwnerLockedException.php',
327333
'OCP\\Files\\Mount\\IMountManager' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountManager.php',
328334
'OCP\\Files\\Mount\\IMountPoint' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountPoint.php',
329335
'OCP\\Files\\Node' => __DIR__ . '/../../..' . '/lib/public/Files/Node.php',
@@ -1158,6 +1164,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
11581164
'OC\\Files\\Config\\UserMountCacheListener' => __DIR__ . '/../../..' . '/lib/private/Files/Config/UserMountCacheListener.php',
11591165
'OC\\Files\\FileInfo' => __DIR__ . '/../../..' . '/lib/private/Files/FileInfo.php',
11601166
'OC\\Files\\Filesystem' => __DIR__ . '/../../..' . '/lib/private/Files/Filesystem.php',
1167+
'OC\\Files\\Lock\\LockManager' => __DIR__ . '/../../..' . '/lib/private/Files/Lock/LockManager.php',
11611168
'OC\\Files\\Mount\\CacheMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/CacheMountProvider.php',
11621169
'OC\\Files\\Mount\\LocalHomeMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/LocalHomeMountProvider.php',
11631170
'OC\\Files\\Mount\\Manager' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/Manager.php',
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace OC\Files\Lock;
4+
5+
use OCP\Files\Lock\ILock;
6+
use OCP\Files\Lock\ILockManager;
7+
use OCP\Files\Lock\ILockProvider;
8+
use OCP\Files\Lock\LockScope;
9+
use OCP\PreConditionNotMetException;
10+
11+
class LockManager implements ILockManager {
12+
private ?ILockProvider $lockProvider = null;
13+
private ?LockScope $lockInScope = null;
14+
15+
public function registerLockProvider(ILockProvider $lockProvider): void {
16+
if ($this->lockProvider) {
17+
throw new PreConditionNotMetException('There is already a registered lock provider');
18+
}
19+
20+
$this->lockProvider = $lockProvider;
21+
}
22+
23+
public function isLockProviderAvailable(): bool {
24+
return $this->lockProvider !== null;
25+
}
26+
27+
public function runInScope(LockScope $lock, callable $callback): void {
28+
if (!$this->lockProvider) {
29+
$callback();
30+
return;
31+
}
32+
33+
if ($this->lockInScope) {
34+
throw new PreConditionNotMetException('Could not obtain lock scope as already in use by ' . $this->lockInScope);
35+
}
36+
37+
try {
38+
$this->lockInScope = $lock;
39+
$callback();
40+
} finally {
41+
$this->lockInScope = null;
42+
}
43+
}
44+
45+
public function getLockInScope(): ?LockScope {
46+
return $this->lockInScope;
47+
}
48+
49+
public function getLocks(int $fileId): array {
50+
if (!$this->lockProvider) {
51+
throw new PreConditionNotMetException('No lock provider available');
52+
}
53+
54+
return $this->lockProvider->getLocks($fileId);
55+
}
56+
57+
public function lock(LockScope $lockInfo): ILock {
58+
if (!$this->lockProvider) {
59+
throw new PreConditionNotMetException('No lock provider available');
60+
}
61+
62+
return $this->lockProvider->lock($lockInfo);
63+
}
64+
65+
public function unlock(LockScope $lockInfo): void {
66+
if (!$this->lockProvider) {
67+
throw new PreConditionNotMetException('No lock provider available');
68+
}
69+
70+
$this->lockProvider->unlock($lockInfo);
71+
}
72+
}

lib/private/Server.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
use OC\Federation\CloudIdManager;
8989
use OC\Files\Config\UserMountCache;
9090
use OC\Files\Config\UserMountCacheListener;
91+
use OC\Files\Lock\LockManager;
9192
use OC\Files\Mount\CacheMountProvider;
9293
use OC\Files\Mount\LocalHomeMountProvider;
9394
use OC\Files\Mount\ObjectHomeMountProvider;
@@ -175,6 +176,7 @@
175176
use OCP\Files\IMimeTypeDetector;
176177
use OCP\Files\IMimeTypeLoader;
177178
use OCP\Files\IRootFolder;
179+
use OCP\Files\Lock\ILockManager;
178180
use OCP\Files\Mount\IMountManager;
179181
use OCP\Files\NotFoundException;
180182
use OCP\Files\Storage\IStorageFactory;
@@ -1113,6 +1115,10 @@ public function __construct($webRoot, \OC\Config $config) {
11131115
/** @deprecated 19.0.0 */
11141116
$this->registerDeprecatedAlias('LockingProvider', ILockingProvider::class);
11151117

1118+
$this->registerService(ILockManager::class, function (Server $c): LockManager {
1119+
return new LockManager();
1120+
});
1121+
11161122
$this->registerAlias(ILockdownManager::class, 'LockdownManager');
11171123
$this->registerService(SetupManager::class, function ($c) {
11181124
// create the setupmanager through the mount manager to resolve the cyclic dependency

lib/public/DirectEditing/IManager.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,10 @@ public function cleanup(): int;
9191
* @return bool
9292
*/
9393
public function isEnabled(): bool;
94+
95+
/**
96+
* @since 24.0.0
97+
* @return IEditor[]
98+
*/
99+
public function getEditors(): array;
94100
}

lib/public/Files/Lock/ILock.php

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2022 Julius Härtl <[email protected]>
7+
*
8+
* @author Julius Härtl <[email protected]>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OCP\Files\Lock;
28+
29+
/**
30+
* @since 24.0.0
31+
*/
32+
interface ILock {
33+
34+
/**
35+
* User owned manual lock
36+
*
37+
* This lock type is initiated by a user manually through the web UI or clients
38+
* and will limit editing capabilities on the file to the lock owning user.
39+
*
40+
* @since 24.0.0
41+
*/
42+
public const TYPE_USER = 0;
43+
44+
/**
45+
* App owned lock
46+
*
47+
* This lock type is created by collaborative apps like Text or Office to avoid
48+
* outside changes through WevDAV or other apps.
49+
* @since 24.0.0
50+
*
51+
*/
52+
public const TYPE_APP = 1;
53+
54+
/**
55+
* Token owned lock
56+
*
57+
* This lock type will bind the ownership to the provided lock token. Any request
58+
* that aims to modify the file will be required to sent the token, the user
59+
* itself is not able to write to files without the token. This will allow
60+
* to limit the locking to an individual client.
61+
*
62+
* @since 24.0.0
63+
*/
64+
public const TYPE_TOKEN = 2;
65+
66+
/**
67+
* WebDAV Lock scope exclusive
68+
*
69+
* @since 24.0.0
70+
*/
71+
public const LOCK_EXCLUSIVE = 1;
72+
73+
/**
74+
* WebDAV Lock scope shared
75+
*
76+
* @since 24.0.0
77+
*/
78+
public const LOCK_SHARED = 2;
79+
80+
/**
81+
* Lock only the resource the lock is applied to
82+
*
83+
* @since 24.0.0
84+
*/
85+
public const LOCK_DEPTH_ZERO = 0;
86+
87+
/**
88+
* Lock app resources under the locked one with infinite depth
89+
*
90+
* @since 24.0.0
91+
*/
92+
public const LOCK_DEPTH_INFINITE = -1;
93+
94+
/**
95+
* Type of the lock
96+
*
97+
* @psalm-return ILock::TYPE_*
98+
* @since 24.0.0
99+
*/
100+
public function getType(): int;
101+
102+
/**
103+
* Owner that holds the lock
104+
*
105+
* Depending on the lock type this is:
106+
* - ILock::TYPE_USER: A user id
107+
* - ILock::TYPE_APP: An app id
108+
* - ILock::TYPE_TOKEN: A user id
109+
*
110+
* @since 24.0.0
111+
*/
112+
public function getOwner(): string;
113+
114+
/**
115+
* File id that the lock is holding
116+
*
117+
* @since 24.0.0
118+
*/
119+
public function getFileId(): int;
120+
121+
/**
122+
* Timeout of the lock in seconds starting from the created at time
123+
*
124+
* @since 24.0.0
125+
*/
126+
public function getTimeout(): int;
127+
128+
/**
129+
* Unix timestamp of the lock creation time
130+
*
131+
* @since 24.0.0
132+
*/
133+
public function getCreatedAt(): int;
134+
135+
/**
136+
* Token string as a unique identifier for the lock, usually a UUID
137+
*
138+
* @since 24.0.0
139+
*/
140+
public function getToken(): string;
141+
142+
/**
143+
* Lock depth to apply the lock to child resources
144+
*
145+
* @since 24.0.0
146+
*/
147+
public function getDepth(): int;
148+
149+
/**
150+
* WebDAV lock scope
151+
*
152+
* @since 24.0.0
153+
* @psalm-return ILock::LOCK_EXCLUSIVE|ILock::LOCK_SHARED
154+
*/
155+
public function getScope(): int;
156+
157+
/**
158+
* String representation of the lock to identify it through logging
159+
*
160+
* @since 24.0.0
161+
*/
162+
public function __toString(): string;
163+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2022 Julius Härtl <[email protected]>
7+
*
8+
* @author Julius Härtl <[email protected]>
9+
*
10+
* @license GNU AGPL version 3 or any later version
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU Affero General Public License as
14+
* published by the Free Software Foundation, either version 3 of the
15+
* License, or (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU Affero General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU Affero General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OCP\Files\Lock;
28+
29+
use OCP\PreConditionNotMetException;
30+
31+
/**
32+
* Manage app integrations with files_lock with collaborative editors
33+
*
34+
* The OCP parts are mainly for exposing the ability to lock/unlock for apps and
35+
* to give the files_lock app a way to register and then be triggered by the apps
36+
* while the actual locking implementation is kept in the LockProvider and DAV
37+
* plugin from files_lock app.
38+
*
39+
* @since 24.0.0
40+
*/
41+
interface ILockManager extends ILockProvider {
42+
43+
/**
44+
* @throws PreConditionNotMetException if there is already a lock provider registered
45+
* @since 24.0.0
46+
*/
47+
public function registerLockProvider(ILockProvider $lockProvider): void;
48+
49+
/**
50+
* @return bool
51+
* @since 24.0.0
52+
*/
53+
public function isLockProviderAvailable(): bool;
54+
55+
/**
56+
* Run within the scope of a given lock condition
57+
*
58+
* The callback will also be executed if no lock provider is present
59+
*
60+
* @since 24.0.0
61+
*/
62+
public function runInScope(LockScope $lock, callable $callback): void;
63+
64+
/**
65+
* @throws NoLockProviderException if there is no lock provider available
66+
* @since 24.0.0
67+
*/
68+
public function getLockInScope(): ?LockScope;
69+
}

0 commit comments

Comments
 (0)