Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable10] Fix persistent locking handling in public links #34227

Merged
merged 2 commits into from
Jan 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions apps/dav/lib/Files/FileLocksBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,14 @@ public function getLocks($uri, $returnChildLocks) {
/** @var IPersistentLockingStorage $storage */
$locks = $storage->getLocks($node->getFileInfo()->getInternalPath(), $returnChildLocks);
} catch (NotFound $e) {
// get parent storage and check for locks on the target path
list($parentPath, $childPath) = \Sabre\Uri\split($uri);
if ($parentPath === '') {
if ($uri === '') {
// no more parents
return [];
}

// get parent storage and check for locks on the target path
list($parentPath, $childPath) = \Sabre\Uri\split($uri);

try {
$node = $this->tree->getNodeForPath($parentPath);
} catch (NotFound $e) {
Expand Down
12 changes: 12 additions & 0 deletions apps/dav/tests/unit/Files/FileLocksBackendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use Sabre\DAV\Locks\LockInfo;
use Sabre\DAV\Tree;
use Test\TestCase;
use OCA\DAV\Connector\Sabre\Directory;

class FileLocksBackendTest extends TestCase {
const CREATION_TIME = 164419200;
Expand All @@ -56,6 +57,17 @@ public function setUp() {

$this->tree = $this->createMock(Tree::class);
$this->tree->method('getNodeForPath')->willReturnCallback(function ($uri) {
// root node
if ($uri === '') {
$storage = $this->createMock([IPersistentLockingStorage::class, IStorage::class]);
$storage->method('instanceOfStorage')->willReturn(true);
$storage->method('getLocks')->willReturn([]);
$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->method('getStorage')->willReturn($storage);
$file = $this->createMock(Directory::class);
$file->method('getFileInfo')->willReturn($fileInfo);
return $file;
}
if ($uri === 'unknown-file.txt') {
throw new NotFound();
}
Expand Down
14 changes: 11 additions & 3 deletions apps/files/js/filelockplugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,24 @@
* @return {Object} parsed values in associative array
*/
function parseLockNode(xmlvalue) {
return {
var lockInfo = {
lockscope: getChildNodeLocalName(xmlvalue.getElementsByTagNameNS(NS_DAV, 'lockscope')[0]),
locktype: getChildNodeLocalName(xmlvalue.getElementsByTagNameNS(NS_DAV, 'locktype')[0]),
lockroot: getHrefNodeContents(xmlvalue.getElementsByTagNameNS(NS_DAV, 'lockroot')[0]),
// string, as it can also be "infinite"
depth: xmlvalue.getElementsByTagNameNS(NS_DAV, 'depth')[0].textContent,
timeout: xmlvalue.getElementsByTagNameNS(NS_DAV, 'timeout')[0].textContent,
locktoken: getHrefNodeContents(xmlvalue.getElementsByTagNameNS(NS_DAV, 'locktoken')[0]),
owner: xmlvalue.getElementsByTagNameNS(NS_DAV, 'owner')[0].textContent
locktoken: getHrefNodeContents(xmlvalue.getElementsByTagNameNS(NS_DAV, 'locktoken')[0])
};

var owner = null;
var ownerEl = xmlvalue.getElementsByTagNameNS(NS_DAV, 'owner');
if (ownerEl && ownerEl.length) {
owner = ownerEl[0].textContent;
}

lockInfo.owner = owner || t('files', 'Unknown user');
return lockInfo;
}

function getHrefNodeContents(node) {
Expand Down
7 changes: 6 additions & 1 deletion apps/files/js/locktabview.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
var client = OC.Files.getClient();

return _.map(locks, function(lock, index) {
var path = client.getRelativePath(lock.lockroot) || lock.lockroot;
var path = client.getRelativePath(lock.lockroot);
if (path === null) {
path = lock.lockroot;
} else if (path === '') {
path = '/';
}

// TODO: what if user in root doesn't match ?

Expand Down
37 changes: 32 additions & 5 deletions apps/files/tests/js/filelockpluginSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ describe('OCA.Files.LockPlugin tests', function() {
afterEach(function() {
requestStub.restore();
});
it('parses lock information from response XML to JSON', function(done) {

function makeLockXml(owner) {
var xml =
'<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns">' +
' <d:response>' +
Expand Down Expand Up @@ -170,8 +170,12 @@ describe('OCA.Files.LockPlugin tests', function() {
' <d:timeout>Second-12345</d:timeout>' +
' <d:locktoken>' +
' <d:href>tehtoken</d:href>' +
' </d:locktoken>' +
' <d:owner>lock owner</d:owner>' +
' </d:locktoken>';
if (owner !== null) {
xml += ' <d:owner>' + owner + '</d:owner>';
}

xml +=
' </d:activelock>' +
' </d:lockdiscovery>' +
' </d:prop>' +
Expand All @@ -180,7 +184,30 @@ describe('OCA.Files.LockPlugin tests', function() {
' </d:response>' +
'</d:multistatus>';

xml = dav.Client.prototype.parseMultiStatus(xml);
return xml;
}

it('parses lock information from response XML to JSON', function(done) {
var xml = dav.Client.prototype.parseMultiStatus(makeLockXml('lock owner'));
var promise = fileList.reload();
requestDeferred.resolve({
status: 207,
body: xml
});

promise.then(function(status, response) {
var $tr = fileList.findFileEl('One.txt');
var data = fileList.elementToFile($tr);
expect(data.activeLocks.length).toEqual(1);
expect(data.activeLocks[0]).toEqual(testLockData);
done();
});
});

it('defaults to unknown user if owner info is not found in lock information', function(done) {
testLockData.owner = 'Unknown user';

var xml = dav.Client.prototype.parseMultiStatus(makeLockXml(null));
var promise = fileList.reload();
requestDeferred.resolve({
status: 207,
Expand Down
3 changes: 3 additions & 0 deletions tests/TestHelpers/WebDavHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@ public static function makeDavRequest(
public static function getDavPath(
$user, $davPathVersionToUse = 1, $type = "files"
) {
if ($type === "public-files") {
return "public.php/webdav/";
}
if ($davPathVersionToUse === 1) {
return "remote.php/webdav/";
} elseif ($davPathVersionToUse === 2) {
Expand Down
45 changes: 42 additions & 3 deletions tests/acceptance/features/bootstrap/WebDavLockingContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,21 @@ class WebDavLockingContext implements Context {
* @param string $user
* @param string $file
* @param TableNode $properties table with no heading with | property | value |
* @param boolean $public if the file is in a public share or not
*
* @return void
*/
public function lockFileUsingWebDavAPI($user, $file, TableNode $properties) {
public function lockFileUsingWebDavAPI(
$user, $file, TableNode $properties, $public = false
) {
$baseUrl = $this->featureContext->getBaseUrl();
$password = $this->featureContext->getPasswordForUser($user);
if ($public === true) {
$type = "public-files";
$password = null;
} else {
$type = "files";
$password = $this->featureContext->getPasswordForUser($user);
}
$body
= "<?xml version='1.0' encoding='UTF-8'?>" .
"<d:lockinfo xmlns:d='DAV:'> ";
Expand All @@ -72,14 +81,44 @@ public function lockFileUsingWebDavAPI($user, $file, TableNode $properties) {
$body .= "</d:lockinfo>";
$response = WebDavHelper::makeDavRequest(
$baseUrl, $user, $password, "LOCK", $file, $headers, $body, null,
$this->featureContext->getDavPathVersion()
$this->featureContext->getDavPathVersion(), $type
);

$responseXml = $this->featureContext->getResponseXml($response);
$responseXml->registerXPathNamespace('d', 'DAV:');
$xmlPart = $responseXml->xpath("//d:locktoken/d:href");
$this->tokenOfLastLock[$user][$file] = (string)$xmlPart[0];
}

/**
* @When the public locks the last public shared file/folder using the WebDAV API setting following properties
*
* @param TableNode $properties
*
* @return void
*/
public function publicLocksLastSharedFile(TableNode $properties) {
$this->lockFileUsingWebDavAPI(
(string)$this->featureContext->getLastShareData()->data->token,
"/", $properties, true
);
}

/**
* @When the public locks :file in the last public shared folder using the WebDAV API setting following properties
*
* @param string $file
* @param TableNode $properties
*
* @return void
*/
public function publicLocksFileLastSharedFolder($file, TableNode $properties) {
$this->lockFileUsingWebDavAPI(
(string)$this->featureContext->getLastShareData()->data->token,
$file, $properties, true
);
}

/**
* @When the user :user unlocks the last created lock of file/folder :file using the WebDAV API
*
Expand Down
34 changes: 34 additions & 0 deletions tests/acceptance/features/webUIWebdavLocks/locks.feature
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,40 @@ Feature: Locks
And folder "simple-folder" should be marked as locked by user "user-with-email ([email protected])" in the locks tab of the details panel on the webUI
And file "data.zip" should be marked as locked by user "user-with-email ([email protected])" in the locks tab of the details panel on the webUI

Scenario: setting a lock on a publicly shared file shows the lock symbols at the correct file
Given user "brand-new-user" has created a public link share of file "data.zip"
When the public locks the last public shared file using the WebDAV API setting following properties
| lockscope | shared |
And the user browses to the files page
Then file "data.zip" should be marked as locked on the webUI
And file "data.zip" should be marked as locked by user "Unknown user" in the locks tab of the details panel on the webUI
But file "data.tar.gz" should not be marked as locked on the webUI

Scenario: setting a lock on a publicly shared folder shows the lock symbols at the correct files/folder
Given user "brand-new-user" has created a public link share of folder "simple-folder"
When the public locks the last public shared folder using the WebDAV API setting following properties
| lockscope | shared |
And the user browses to the files page
Then folder "simple-folder" should be marked as locked on the webUI
And folder "simple-folder" should be marked as locked by user "Unknown user" in the locks tab of the details panel on the webUI
But folder "simple-empty-folder" should not be marked as locked on the webUI
When the user opens folder "simple-folder" using the webUI
Then file "data.zip" should be marked as locked on the webUI
And file "data.zip" should be marked as locked by user "Unknown user" in the locks tab of the details panel on the webUI
And file "data.tar.gz" should be marked as locked on the webUI

Scenario: setting a lock on a file in a publicly shared folder shows the lock symbols at the correct files
Given user "brand-new-user" has created a public link share of folder "simple-folder"
When the public locks "data.zip" in the last public shared folder using the WebDAV API setting following properties
| lockscope | shared |
And the user browses to the files page
Then folder "simple-folder" should not be marked as locked on the webUI
And folder "simple-empty-folder" should not be marked as locked on the webUI
When the user opens folder "simple-folder" using the webUI
Then file "data.zip" should be marked as locked on the webUI
And file "data.zip" should be marked as locked by user "Unknown user" in the locks tab of the details panel on the webUI
And file "data.tar.gz" should not be marked as locked on the webUI

Scenario: setting a lock shows the lock symbols at the correct files/folders on the favorites page
Given the user "brand-new-user" has locked folder "simple-folder" setting following properties
| lockscope | shared |
Expand Down