summaryrefslogtreecommitdiffstats
path: root/apps/dav
diff options
context:
space:
mode:
authorRoeland Jago Douma <rullzer@users.noreply.github.com>2019-12-05 11:04:33 +0100
committerGitHub <noreply@github.com>2019-12-05 11:04:33 +0100
commit63cb31542d9db663f627a17278030b18ed61bc6a (patch)
tree624c5fce9492da4b4f6836a4a1415ba979055af0 /apps/dav
parent90401e573e44a4a44d4ce1dc47b534cfb4a9ff1f (diff)
parentbf50342c10dd3093417329865008e7be605135c5 (diff)
downloadnextcloud-server-63cb31542d9db663f627a17278030b18ed61bc6a.tar.gz
nextcloud-server-63cb31542d9db663f627a17278030b18ed61bc6a.zip
Merge pull request #17941 from nextcloud/search-by-owner
Allow filtering the search results to the users home storage
Diffstat (limited to 'apps/dav')
-rw-r--r--apps/dav/lib/Files/FileSearchBackend.php82
-rw-r--r--apps/dav/tests/unit/Files/FileSearchBackendTest.php79
2 files changed, 154 insertions, 7 deletions
diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php
index 3eba158c944..6c1f279c6bd 100644
--- a/apps/dav/lib/Files/FileSearchBackend.php
+++ b/apps/dav/lib/Files/FileSearchBackend.php
@@ -119,6 +119,7 @@ class FileSearchBackend implements ISearchBackend {
new SearchPropertyDefinition(FilesPlugin::SIZE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
new SearchPropertyDefinition(TagsPlugin::FAVORITE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_BOOLEAN),
new SearchPropertyDefinition(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, true, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
+ new SearchPropertyDefinition(FilesPlugin::OWNER_ID_PROPERTYNAME, true, true, false),
// select only properties
new SearchPropertyDefinition('{DAV:}resourcetype', false, true, false),
@@ -126,7 +127,6 @@ class FileSearchBackend implements ISearchBackend {
new SearchPropertyDefinition(FilesPlugin::CHECKSUMS_PROPERTYNAME, false, true, false),
new SearchPropertyDefinition(FilesPlugin::PERMISSIONS_PROPERTYNAME, false, true, false),
new SearchPropertyDefinition(FilesPlugin::GETETAG_PROPERTYNAME, false, true, false),
- new SearchPropertyDefinition(FilesPlugin::OWNER_ID_PROPERTYNAME, false, true, false),
new SearchPropertyDefinition(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, false, true, false),
new SearchPropertyDefinition(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, false, true, false),
new SearchPropertyDefinition(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_BOOLEAN),
@@ -169,10 +169,12 @@ class FileSearchBackend implements ISearchBackend {
return new SearchResult($davNode, $path);
}, $results);
- // Sort again, since the result from multiple storages is appended and not sorted
- usort($nodes, function (SearchResult $a, SearchResult $b) use ($search) {
- return $this->sort($a, $b, $search->orderBy);
- });
+ if (!$query->limitToHome()) {
+ // Sort again, since the result from multiple storages is appended and not sorted
+ usort($nodes, function (SearchResult $a, SearchResult $b) use ($search) {
+ return $this->sort($a, $b, $search->orderBy);
+ });
+ }
// If a limit is provided use only return that number of files
if ($search->limit->maxResults !== 0) {
@@ -267,11 +269,29 @@ class FileSearchBackend implements ISearchBackend {
* @param Query $query
* @return ISearchQuery
*/
- private function transformQuery(Query $query) {
+ private function transformQuery(Query $query): ISearchQuery {
// TODO offset
$limit = $query->limit;
$orders = array_map([$this, 'mapSearchOrder'], $query->orderBy);
- return new SearchQuery($this->transformSearchOperation($query->where), (int)$limit->maxResults, 0, $orders, $this->user);
+
+ $limitHome = false;
+ $ownerProp = $this->extractWhereValue($query->where, FilesPlugin::OWNER_ID_PROPERTYNAME, Operator::OPERATION_EQUAL);
+ if ($ownerProp !== null) {
+ if ($ownerProp === $this->user->getUID()) {
+ $limitHome = true;
+ } else {
+ throw new \InvalidArgumentException("Invalid search value for '{http://owncloud.org/ns}owner-id', only the current user id is allowed");
+ }
+ }
+
+ return new SearchQuery(
+ $this->transformSearchOperation($query->where),
+ (int)$limit->maxResults,
+ 0,
+ $orders,
+ $this->user,
+ $limitHome
+ );
}
/**
@@ -360,4 +380,52 @@ class FileSearchBackend implements ISearchBackend {
return $value;
}
}
+
+ /**
+ * Get a specific property from the were clause
+ */
+ private function extractWhereValue(Operator &$operator, string $propertyName, string $comparison, bool $acceptableLocation = true): ?string {
+ switch ($operator->type) {
+ case Operator::OPERATION_AND:
+ case Operator::OPERATION_OR:
+ case Operator::OPERATION_NOT:
+ foreach ($operator->arguments as &$argument) {
+ $value = $this->extractWhereValue($argument, $propertyName, $comparison, $acceptableLocation && $operator->type === Operator::OPERATION_AND);
+ if ($value !== null) {
+ return $value;
+ }
+ }
+ return null;
+ case Operator::OPERATION_EQUAL:
+ case Operator::OPERATION_GREATER_OR_EQUAL_THAN:
+ case Operator::OPERATION_GREATER_THAN:
+ case Operator::OPERATION_LESS_OR_EQUAL_THAN:
+ case Operator::OPERATION_LESS_THAN:
+ case Operator::OPERATION_IS_LIKE:
+ if ($operator->arguments[0]->name === $propertyName) {
+ if ($operator->type === $comparison) {
+ if ($acceptableLocation) {
+ if ($operator->arguments[1] instanceof Literal) {
+ $value = $operator->arguments[1]->value;
+
+ // to remove the comparison from the query, we replace it with an empty AND
+ $operator = new Operator(Operator::OPERATION_AND);
+
+ return $value;
+ } else {
+ throw new \InvalidArgumentException("searching by '$propertyName' is only allowed with a literal value");
+ }
+ } else{
+ throw new \InvalidArgumentException("searching by '$propertyName' is not allowed inside a '{DAV:}or' or '{DAV:}not'");
+ }
+ } else {
+ throw new \InvalidArgumentException("searching by '$propertyName' is only allowed inside a '$comparison'");
+ }
+ } else {
+ return null;
+ }
+ default:
+ return null;
+ }
+ }
}
diff --git a/apps/dav/tests/unit/Files/FileSearchBackendTest.php b/apps/dav/tests/unit/Files/FileSearchBackendTest.php
index 723fb9f4049..d4b70d61c6f 100644
--- a/apps/dav/tests/unit/Files/FileSearchBackendTest.php
+++ b/apps/dav/tests/unit/Files/FileSearchBackendTest.php
@@ -35,7 +35,9 @@ use OCA\DAV\Files\FileSearchBackend;
use OCP\Files\FileInfo;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
+use OCP\Files\Search\ISearchBinaryOperator;
use OCP\Files\Search\ISearchComparison;
+use OCP\Files\Search\ISearchQuery;
use OCP\IUser;
use OCP\Share\IManager;
use SearchDAV\Backend\SearchPropertyDefinition;
@@ -308,4 +310,81 @@ class FileSearchBackendTest extends TestCase {
$query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_EQUAL, '{DAV:}displayname', 'foo');
$this->search->search($query);
}
+
+ public function testSearchLimitOwnerBasic() {
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->willReturn($this->davFolder);
+
+ /** @var ISearchQuery|null $receivedQuery */
+ $receivedQuery = null;
+ $this->searchFolder
+ ->method('search')
+ ->will($this->returnCallback(function ($query) use (&$receivedQuery) {
+ $receivedQuery = $query;
+ return [
+ new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path')
+ ];
+ }));
+
+ $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_EQUAL, FilesPlugin::OWNER_ID_PROPERTYNAME, $this->user->getUID());
+ $this->search->search($query);
+
+ $this->assertNotNull($receivedQuery);
+ $this->assertTrue($receivedQuery->limitToHome());
+
+ /** @var ISearchBinaryOperator $operator */
+ $operator = $receivedQuery->getSearchOperation();
+ $this->assertInstanceOf(ISearchBinaryOperator::class, $operator);
+ $this->assertEquals(ISearchBinaryOperator::OPERATOR_AND, $operator->getType());
+ $this->assertEmpty($operator->getArguments());
+ }
+
+ public function testSearchLimitOwnerNested() {
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->willReturn($this->davFolder);
+
+ /** @var ISearchQuery|null $receivedQuery */
+ $receivedQuery = null;
+ $this->searchFolder
+ ->method('search')
+ ->will($this->returnCallback(function ($query) use (&$receivedQuery) {
+ $receivedQuery = $query;
+ return [
+ new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path')
+ ];
+ }));
+
+ $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_EQUAL, FilesPlugin::OWNER_ID_PROPERTYNAME, $this->user->getUID());
+ $query->where = new \SearchDAV\Query\Operator(
+ \SearchDAV\Query\Operator::OPERATION_AND,
+ [
+ new \SearchDAV\Query\Operator(
+ \SearchDAV\Query\Operator::OPERATION_EQUAL,
+ [new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true), new \SearchDAV\Query\Literal('image/png')]
+ ),
+ new \SearchDAV\Query\Operator(
+ \SearchDAV\Query\Operator::OPERATION_EQUAL,
+ [new SearchPropertyDefinition(FilesPlugin::OWNER_ID_PROPERTYNAME, true, true, true), new \SearchDAV\Query\Literal($this->user->getUID())]
+ )
+ ]
+ );
+ $this->search->search($query);
+
+ $this->assertNotNull($receivedQuery);
+ $this->assertTrue($receivedQuery->limitToHome());
+
+ /** @var ISearchBinaryOperator $operator */
+ $operator = $receivedQuery->getSearchOperation();
+ $this->assertInstanceOf(ISearchBinaryOperator::class, $operator);
+ $this->assertEquals(ISearchBinaryOperator::OPERATOR_AND, $operator->getType());
+ $this->assertCount(2, $operator->getArguments());
+
+ /** @var ISearchBinaryOperator $operator */
+ $operator = $operator->getArguments()[1];
+ $this->assertInstanceOf(ISearchBinaryOperator::class, $operator);
+ $this->assertEquals(ISearchBinaryOperator::OPERATOR_AND, $operator->getType());
+ $this->assertEmpty($operator->getArguments());
+ }
}