Skip to content

Commit 743f645

Browse files
committed
feat: Add API parameters to specify the lock type
Signed-off-by: Julius Härtl <[email protected]> tmp: bump max version Signed-off-by: Julius Härtl <[email protected]>
1 parent 8ff815c commit 743f645

File tree

5 files changed

+51
-21
lines changed

5 files changed

+51
-21
lines changed

README.md

+33-1
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,30 @@ Locks are separated into three different types:
3232
This lock type is initiated by a user manually through the WebUI or Clients and will limit editing capabilities on the file to the lock owning user.
3333
- **1 App owned lock:**
3434
This lock type is created by collaborative apps like Text or Office to avoid outside changes through WevDAV or other apps.
35-
- **2 Token owned lock:** (not implemented yet) This lock type will bind the ownership to the provided lock token. Any request that aims to modify the file will be required to sent the token, the user itself is not able to write to files without the token. This will allow to limit the locking to an individual client.
35+
- **2 Token owned lock:** This lock type will bind the ownership to the provided lock token. Any request that aims to modify the file will be required to sent the token, the user itself is not able to write to files without the token. This will allow to limit the locking to an individual client.
36+
- This is mostly used for automatic client locking, e.g. when a file is opened in a client or with WebDAV clients that support native WebDAV locking. The lock token can be skipped on follow up requests using the OCS API or the `X-User-Lock` header for WebDAV requests, but in that case the server will not be able to validate the lock ownership when unlocking the file from the client.
3637

3738
### Capability
3839

3940
If locking is available the app will expose itself through the capabilties endpoint under the files key:
41+
42+
Make sure to validate that also the key exists in the capabilities response, not just check the value as on older versions the entry might be missing completely.
43+
4044
```
4145
curl http://admin:[email protected]/ocs/v1.php/cloud/capabilities\?format\=json \
4246
-H 'OCS-APIRequest: true' \
4347
| jq .ocs.data.capabilities.files
4448
{
4549
...
4650
"locking": "1.0",
51+
"api-feature-lock-type" => true,
4752
...
4853
}
4954
```
5055

56+
- `locking`: The version of the locking API
57+
- `api-feature-lock-type`: Feature flag, whether the server supports the `lockType` parameter for the OCS API or `X-User-Lock-Type` header for WebDAV requests.
58+
5159
### WebDAV: Fetching lock details
5260

5361
WebDAV returns the following additional properties if requests through a `PROPFIND`:
@@ -88,6 +96,11 @@ curl -X LOCK \
8896
--header 'X-User-Lock: 1'
8997
```
9098

99+
#### Headers
100+
101+
- `X-User-Lock`: Indicate that locking shall ignore token requirements that the WebDAV standard required.
102+
- `X-User-Lock-Type`: The type of the lock (see description above)
103+
91104
#### Response
92105

93106
The response will give back the updated properties after obtaining the lock with a `200 Success` status code or the existing lock details in case of a `423 Locked` status.
@@ -123,6 +136,11 @@ curl -X UNLOCK \
123136
--header 'X-User-Lock: 1'
124137
```
125138

139+
#### Headers
140+
141+
- `X-User-Lock`: Indicate that locking shall ignore token requirements that the WebDAV standard required.
142+
- `X-User-Lock-Type`: The type of the lock (see description above)
143+
126144
#### Response
127145

128146
```xml
@@ -155,6 +173,13 @@ curl -X UNLOCK \
155173
curl -X PUT 'http://admin:[email protected]/ocs/v2.php/apps/files_lock/lock/123' -H 'OCS-APIREQUEST: true'`
156174
```
157175

176+
#### Parameters
177+
178+
- `lockType` (optional): The type of the lock. Defaults to `0` (user owned manual lock). Possible values are:
179+
- `0`: User owned manual lock
180+
- `1`: App owned lock
181+
- `2`: Token owned lock
182+
158183
#### Success
159184
```
160185
<?xml version="1.0"?>
@@ -193,6 +218,13 @@ curl -X PUT 'http://admin:[email protected]/ocs/v2.php/apps/files_lock/lock/
193218
curl -X DELETE 'http://admin:[email protected]/ocs/v2.php/apps/files_lock/lock/123' -H 'OCS-APIREQUEST: true'
194219
```
195220

221+
#### Parameters
222+
223+
- `lockType` (optional): The type of the lock. Defaults to `0` (user owned manual lock). Possible values are:
224+
- `0`: User owned manual lock
225+
- `1`: App owned lock
226+
- `2`: Token owned lock
227+
196228
#### Success
197229
```
198230
<?xml version="1.0"?>

lib/Capability.php

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public function getCapabilities() {
99
return [
1010
'files' => [
1111
'locking' => '1.0',
12+
'api-feature-lock-type' => true,
1213
]
1314
];
1415
}

lib/Controller/LockController.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@ public function __construct(
9090
*
9191
* @return DataResponse
9292
*/
93-
public function locking(string $fileId): DataResponse {
93+
public function locking(string $fileId, int $lockType = ILock::TYPE_USER): DataResponse {
9494
try {
9595
$user = $this->userSession->getUser();
9696
$file = $this->fileService->getFileFromId($user->getUID(), (int)$fileId);
9797

9898
$lock = $this->lockService->lock(new LockContext(
99-
$file, ILock::TYPE_USER, $user->getUID()
99+
$file, $lockType, $user->getUID()
100100
));
101101

102102
return new DataResponse($lock, Http::STATUS_OK);
@@ -115,7 +115,7 @@ public function locking(string $fileId): DataResponse {
115115
*
116116
* @return DataResponse
117117
*/
118-
public function unlocking(string $fileId): DataResponse {
118+
public function unlocking(string $fileId, int $lockType = ILock::TYPE_USER): DataResponse {
119119
try {
120120
$user = $this->userSession->getUser();
121121
$this->lockService->enableUserOverride();

lib/DAV/LockPlugin.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,14 @@ public function customProperties(PropFind $propFind, INode $node) {
183183

184184
public function httpLock(RequestInterface $request, ResponseInterface $response) {
185185
if ($request->getHeader('X-User-Lock')) {
186+
$lockType = (int)($request->getHeader('X-User-Lock-Type') ?? ILock::TYPE_USER);
186187
$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
187188

188189
$file = $this->fileService->getFileFromAbsoluteUri($this->server->getRequestUri());
189190

190191
try {
191192
$lockInfo = $this->lockService->lock(new LockContext(
192-
$file, ILock::TYPE_USER, $this->userSession->getUser()->getUID()
193+
$file, $lockType, $this->userSession->getUser()->getUID()
193194
));
194195
$response->setStatus(200);
195196
$response->setBody(
@@ -216,14 +217,15 @@ public function httpLock(RequestInterface $request, ResponseInterface $response)
216217

217218
public function httpUnlock(RequestInterface $request, ResponseInterface $response) {
218219
if ($request->getHeader('X-User-Lock')) {
220+
$lockType = (int)($request->getHeader('X-User-Lock-Type') ?? ILock::TYPE_USER);
219221
$response->setHeader('Content-Type', 'application/xml; charset=utf-8');
220222

221223
$file = $this->fileService->getFileFromAbsoluteUri($this->server->getRequestUri());
222224

223225
try {
224226
$this->lockService->enableUserOverride();
225227
$this->lockService->unlock(new LockContext(
226-
$file, ILock::TYPE_USER, $this->userSession->getUser()->getUID()
228+
$file, $lockType, $this->userSession->getUser()->getUID()
227229
));
228230
$response->setStatus(200);
229231
$response->setBody(

lib/Service/LockService.php

+10-15
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,8 @@
4747
use OCP\Files\NotFoundException;
4848
use OCP\IL10N;
4949
use OCP\IUserManager;
50+
use OCP\IUserSession;
5051

51-
/**
52-
* Class LockService
53-
*
54-
* @package OCA\FilesLock\Service
55-
*/
5652
class LockService {
5753
public const PREFIX = 'files_lock';
5854

@@ -61,14 +57,14 @@ class LockService {
6157
use TLogger;
6258

6359

64-
private ?string $userId;
6560
private IUserManager $userManager;
6661
private IL10N $l10n;
6762
private LocksRequest $locksRequest;
6863
private FileService $fileService;
6964
private ConfigService $configService;
7065
private IAppManager $appManager;
7166
private IEventDispatcher $eventDispatcher;
67+
private IUserSession $userSession;
7268

7369

7470
private array $locks = [];
@@ -79,23 +75,23 @@ class LockService {
7975

8076

8177
public function __construct(
82-
$userId,
8378
IL10N $l10n,
8479
IUserManager $userManager,
8580
LocksRequest $locksRequest,
8681
FileService $fileService,
8782
ConfigService $configService,
8883
IAppManager $appManager,
89-
IEventDispatcher $eventDispatcher
84+
IEventDispatcher $eventDispatcher,
85+
IUserSession $userSession
9086
) {
91-
$this->userId = $userId;
9287
$this->l10n = $l10n;
9388
$this->userManager = $userManager;
9489
$this->locksRequest = $locksRequest;
9590
$this->fileService = $fileService;
9691
$this->configService = $configService;
9792
$this->appManager = $appManager;
9893
$this->eventDispatcher = $eventDispatcher;
94+
$this->userSession = $userSession;
9995

10096
$this->setup('app', 'files_lock');
10197
}
@@ -229,7 +225,7 @@ public function enableUserOverride(): void {
229225
}
230226

231227
public function canUnlock(LockContext $request, FileLock $current): void {
232-
$isSameUser = $current->getOwner() === $this->userId;
228+
$isSameUser = $current->getOwner() === $this->userSession->getUser()?->getUID();
233229
$isSameToken = $request->getOwner() === $current->getToken();
234230
$isSameOwner = $request->getOwner() === $current->getOwner();
235231
$isSameType = $request->getType() === $current->getType();
@@ -256,7 +252,7 @@ public function canUnlock(LockContext $request, FileLock $current): void {
256252
'OCA\Files_External\Config\ConfigAdapter'
257253
];
258254
if ($request->getType() === ILock::TYPE_USER
259-
&& $request->getNode()->getOwner()->getUID() === $this->userId
255+
&& $request->getNode()->getOwner()->getUID() === $this->userSession->getUser()?->getUID()
260256
&& !in_array($request->getNode()->getMountPoint()->getMountProvider(), $ignoreFileOwnership)
261257
) {
262258
return;
@@ -274,22 +270,21 @@ public function canUnlock(LockContext $request, FileLock $current): void {
274270
* @throws NotFoundException
275271
* @throws UnauthorizedUnlockException
276272
*/
277-
public function unlockFile(int $fileId, string $userId, bool $force = false): FileLock {
273+
public function unlockFile(int $fileId, string $userId, bool $force = false, int $lockType = ILock::TYPE_USER): FileLock {
278274
$lock = $this->getLockForNodeId($fileId);
279275
if (!$lock) {
280276
throw new LockNotFoundException();
281277
}
282278

283-
$type = ILock::TYPE_USER;
284279
if ($force) {
285280
$userId = $lock->getOwner();
286-
$type = $lock->getType();
281+
$lockType = $lock->getType();
287282
}
288283

289284
$node = $this->fileService->getFileFromId($userId, $fileId);
290285
$lock = new LockContext(
291286
$node,
292-
$type,
287+
$lockType,
293288
$userId,
294289
);
295290
$this->propagateEtag($lock);

0 commit comments

Comments
 (0)