Skip to content

Commit

Permalink
Add public API for owner based locking
Browse files Browse the repository at this point in the history
Signed-off-by: Julius Härtl <jus@bitgrid.net>
  • Loading branch information
juliusknorr committed Apr 4, 2022
1 parent 94004a7 commit 40f1ad6
Show file tree
Hide file tree
Showing 11 changed files with 574 additions and 0 deletions.
7 changes: 7 additions & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,12 @@
'OCP\\Files\\InvalidDirectoryException' => $baseDir . '/lib/public/Files/InvalidDirectoryException.php',
'OCP\\Files\\InvalidPathException' => $baseDir . '/lib/public/Files/InvalidPathException.php',
'OCP\\Files\\LockNotAcquiredException' => $baseDir . '/lib/public/Files/LockNotAcquiredException.php',
'OCP\\Files\\Lock\\ILock' => $baseDir . '/lib/public/Files/Lock/ILock.php',
'OCP\\Files\\Lock\\ILockManager' => $baseDir . '/lib/public/Files/Lock/ILockManager.php',
'OCP\\Files\\Lock\\ILockProvider' => $baseDir . '/lib/public/Files/Lock/ILockProvider.php',
'OCP\\Files\\Lock\\LockScope' => $baseDir . '/lib/public/Files/Lock/LockScope.php',
'OCP\\Files\\Lock\\NoLockProviderException' => $baseDir . '/lib/public/Files/Lock/NoLockProviderException.php',
'OCP\\Files\\Lock\\OwnerLockedException' => $baseDir . '/lib/public/Files/Lock/OwnerLockedException.php',
'OCP\\Files\\Mount\\IMountManager' => $baseDir . '/lib/public/Files/Mount/IMountManager.php',
'OCP\\Files\\Mount\\IMountPoint' => $baseDir . '/lib/public/Files/Mount/IMountPoint.php',
'OCP\\Files\\Node' => $baseDir . '/lib/public/Files/Node.php',
Expand Down Expand Up @@ -1129,6 +1135,7 @@
'OC\\Files\\Config\\UserMountCacheListener' => $baseDir . '/lib/private/Files/Config/UserMountCacheListener.php',
'OC\\Files\\FileInfo' => $baseDir . '/lib/private/Files/FileInfo.php',
'OC\\Files\\Filesystem' => $baseDir . '/lib/private/Files/Filesystem.php',
'OC\\Files\\Lock\\LockManager' => $baseDir . '/lib/private/Files/Lock/LockManager.php',
'OC\\Files\\Mount\\CacheMountProvider' => $baseDir . '/lib/private/Files/Mount/CacheMountProvider.php',
'OC\\Files\\Mount\\LocalHomeMountProvider' => $baseDir . '/lib/private/Files/Mount/LocalHomeMountProvider.php',
'OC\\Files\\Mount\\Manager' => $baseDir . '/lib/private/Files/Mount/Manager.php',
Expand Down
7 changes: 7 additions & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,12 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\Files\\InvalidDirectoryException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidDirectoryException.php',
'OCP\\Files\\InvalidPathException' => __DIR__ . '/../../..' . '/lib/public/Files/InvalidPathException.php',
'OCP\\Files\\LockNotAcquiredException' => __DIR__ . '/../../..' . '/lib/public/Files/LockNotAcquiredException.php',
'OCP\\Files\\Lock\\ILock' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/ILock.php',
'OCP\\Files\\Lock\\ILockManager' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/ILockManager.php',
'OCP\\Files\\Lock\\ILockProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/ILockProvider.php',
'OCP\\Files\\Lock\\LockScope' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/LockScope.php',
'OCP\\Files\\Lock\\NoLockProviderException' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/NoLockProviderException.php',
'OCP\\Files\\Lock\\OwnerLockedException' => __DIR__ . '/../../..' . '/lib/public/Files/Lock/OwnerLockedException.php',
'OCP\\Files\\Mount\\IMountManager' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountManager.php',
'OCP\\Files\\Mount\\IMountPoint' => __DIR__ . '/../../..' . '/lib/public/Files/Mount/IMountPoint.php',
'OCP\\Files\\Node' => __DIR__ . '/../../..' . '/lib/public/Files/Node.php',
Expand Down Expand Up @@ -1158,6 +1164,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Files\\Config\\UserMountCacheListener' => __DIR__ . '/../../..' . '/lib/private/Files/Config/UserMountCacheListener.php',
'OC\\Files\\FileInfo' => __DIR__ . '/../../..' . '/lib/private/Files/FileInfo.php',
'OC\\Files\\Filesystem' => __DIR__ . '/../../..' . '/lib/private/Files/Filesystem.php',
'OC\\Files\\Lock\\LockManager' => __DIR__ . '/../../..' . '/lib/private/Files/Lock/LockManager.php',
'OC\\Files\\Mount\\CacheMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/CacheMountProvider.php',
'OC\\Files\\Mount\\LocalHomeMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/LocalHomeMountProvider.php',
'OC\\Files\\Mount\\Manager' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/Manager.php',
Expand Down
72 changes: 72 additions & 0 deletions lib/private/Files/Lock/LockManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace OC\Files\Lock;

use OCP\Files\Lock\ILock;
use OCP\Files\Lock\ILockManager;
use OCP\Files\Lock\ILockProvider;
use OCP\Files\Lock\LockScope;
use OCP\PreConditionNotMetException;

class LockManager implements ILockManager {
private ?ILockProvider $lockProvider = null;
private ?LockScope $lockInScope = null;

public function registerLockProvider(ILockProvider $lockProvider): void {
if ($this->lockProvider) {
throw new PreConditionNotMetException('There is already a registered lock provider');
}

$this->lockProvider = $lockProvider;
}

public function isLockProviderAvailable(): bool {
return $this->lockProvider !== null;
}

public function runInScope(LockScope $lock, callable $callback): void {
if (!$this->lockProvider) {
$callback();
return;
}

if ($this->lockInScope) {
throw new PreConditionNotMetException('Could not obtain lock scope as already in use by ' . $this->lockInScope);
}

try {
$this->lockInScope = $lock;
$callback();
} finally {
$this->lockInScope = null;
}
}

public function getLockInScope(): ?LockScope {
return $this->lockInScope;
}

public function getLocks(int $fileId): array {
if (!$this->lockProvider) {
throw new PreConditionNotMetException('No lock provider available');
}

return $this->lockProvider->getLocks($fileId);
}

public function lock(LockScope $lockInfo): ILock {
if (!$this->lockProvider) {
throw new PreConditionNotMetException('No lock provider available');
}

return $this->lockProvider->lock($lockInfo);
}

public function unlock(LockScope $lockInfo): void {
if (!$this->lockProvider) {
throw new PreConditionNotMetException('No lock provider available');
}

$this->lockProvider->unlock($lockInfo);
}
}
6 changes: 6 additions & 0 deletions lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
use OC\Federation\CloudIdManager;
use OC\Files\Config\UserMountCache;
use OC\Files\Config\UserMountCacheListener;
use OC\Files\Lock\LockManager;
use OC\Files\Mount\CacheMountProvider;
use OC\Files\Mount\LocalHomeMountProvider;
use OC\Files\Mount\ObjectHomeMountProvider;
Expand Down Expand Up @@ -175,6 +176,7 @@
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IMimeTypeLoader;
use OCP\Files\IRootFolder;
use OCP\Files\Lock\ILockManager;
use OCP\Files\Mount\IMountManager;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorageFactory;
Expand Down Expand Up @@ -1113,6 +1115,10 @@ public function __construct($webRoot, \OC\Config $config) {
/** @deprecated 19.0.0 */
$this->registerDeprecatedAlias('LockingProvider', ILockingProvider::class);

$this->registerService(ILockManager::class, function (Server $c): LockManager {
return new LockManager();
});

$this->registerAlias(ILockdownManager::class, 'LockdownManager');
$this->registerService(SetupManager::class, function ($c) {
// create the setupmanager through the mount manager to resolve the cyclic dependency
Expand Down
6 changes: 6 additions & 0 deletions lib/public/DirectEditing/IManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,10 @@ public function cleanup(): int;
* @return bool
*/
public function isEnabled(): bool;

/**
* @since 24.0.0
* @return IEditor[]
*/
public function getEditors(): array;
}
163 changes: 163 additions & 0 deletions lib/public/Files/Lock/ILock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCP\Files\Lock;

/**
* @since 24.0.0
*/
interface ILock {

/**
* User owned manual lock
*
* This lock type is initiated by a user manually through the web UI or clients
* and will limit editing capabilities on the file to the lock owning user.
*
* @since 24.0.0
*/
public const TYPE_USER = 0;

/**
* App owned lock
*
* This lock type is created by collaborative apps like Text or Office to avoid
* outside changes through WevDAV or other apps.
* @since 24.0.0
*
*/
public const TYPE_APP = 1;

/**
* 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.
*
* @since 24.0.0
*/
public const TYPE_TOKEN = 2;

/**
* WebDAV Lock scope exclusive
*
* @since 24.0.0
*/
public const LOCK_EXCLUSIVE = 1;

/**
* WebDAV Lock scope shared
*
* @since 24.0.0
*/
public const LOCK_SHARED = 2;

/**
* Lock only the resource the lock is applied to
*
* @since 24.0.0
*/
public const LOCK_DEPTH_ZERO = 0;

/**
* Lock app resources under the locked one with infinite depth
*
* @since 24.0.0
*/
public const LOCK_DEPTH_INFINITE = -1;

/**
* Type of the lock
*
* @psalm-return ILock::TYPE_*
* @since 24.0.0
*/
public function getType(): int;

/**
* Owner that holds the lock
*
* Depending on the lock type this is:
* - ILock::TYPE_USER: A user id
* - ILock::TYPE_APP: An app id
* - ILock::TYPE_TOKEN: A user id
*
* @since 24.0.0
*/
public function getOwner(): string;

/**
* File id that the lock is holding
*
* @since 24.0.0
*/
public function getFileId(): int;

/**
* Timeout of the lock in seconds starting from the created at time
*
* @since 24.0.0
*/
public function getTimeout(): int;

/**
* Unix timestamp of the lock creation time
*
* @since 24.0.0
*/
public function getCreatedAt(): int;

/**
* Token string as a unique identifier for the lock, usually a UUID
*
* @since 24.0.0
*/
public function getToken(): string;

/**
* Lock depth to apply the lock to child resources
*
* @since 24.0.0
*/
public function getDepth(): int;

/**
* WebDAV lock scope
*
* @since 24.0.0
* @psalm-return ILock::LOCK_EXCLUSIVE|ILock::LOCK_SHARED
*/
public function getScope(): int;

/**
* String representation of the lock to identify it through logging
*
* @since 24.0.0
*/
public function __toString(): string;
}
69 changes: 69 additions & 0 deletions lib/public/Files/Lock/ILockManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

namespace OCP\Files\Lock;

use OCP\PreConditionNotMetException;

/**
* Manage app integrations with files_lock with collaborative editors
*
* The OCP parts are mainly for exposing the ability to lock/unlock for apps and
* to give the files_lock app a way to register and then be triggered by the apps
* while the actual locking implementation is kept in the LockProvider and DAV
* plugin from files_lock app.
*
* @since 24.0.0
*/
interface ILockManager extends ILockProvider {

/**
* @throws PreConditionNotMetException if there is already a lock provider registered
* @since 24.0.0
*/
public function registerLockProvider(ILockProvider $lockProvider): void;

/**
* @return bool
* @since 24.0.0
*/
public function isLockProviderAvailable(): bool;

/**
* Run within the scope of a given lock condition
*
* The callback will also be executed if no lock provider is present
*
* @since 24.0.0
*/
public function runInScope(LockScope $lock, callable $callback): void;

/**
* @throws NoLockProviderException if there is no lock provider available
* @since 24.0.0
*/
public function getLockInScope(): ?LockScope;
}
Loading

0 comments on commit 40f1ad6

Please sign in to comment.