mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
Merge pull request #27426 from nextcloud/fix/dav-custom-props-no-scope
Allow certain custom DAV props to be readable by everyone
This commit is contained in:
commit
2d1d9393f7
1 changed files with 57 additions and 14 deletions
|
|
@ -31,15 +31,19 @@ use Sabre\DAV\PropertyStorage\Backend\BackendInterface;
|
|||
use Sabre\DAV\PropFind;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Sabre\DAV\Tree;
|
||||
use function array_intersect;
|
||||
|
||||
class CustomPropertiesBackend implements BackendInterface {
|
||||
|
||||
/** @var string */
|
||||
private const TABLE_NAME = 'properties';
|
||||
|
||||
/**
|
||||
* Ignored properties
|
||||
*
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
private $ignoredProperties = [
|
||||
private const IGNORED_PROPERTIES = [
|
||||
'{DAV:}getcontentlength',
|
||||
'{DAV:}getcontenttype',
|
||||
'{DAV:}getetag',
|
||||
|
|
@ -52,6 +56,15 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
'{http://nextcloud.org/ns}is-encrypted',
|
||||
];
|
||||
|
||||
/**
|
||||
* Properties set by one user, readable by all others
|
||||
*
|
||||
* @var array[]
|
||||
*/
|
||||
private const PUBLISHED_READ_ONLY_PROPERTIES = [
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-availability',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var Tree
|
||||
*/
|
||||
|
|
@ -72,7 +85,7 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
private $cache = [];
|
||||
private $userCache = [];
|
||||
|
||||
/**
|
||||
* @param Tree $tree node tree
|
||||
|
|
@ -101,7 +114,7 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
// these might appear
|
||||
$requestedProps = array_diff(
|
||||
$requestedProps,
|
||||
$this->ignoredProperties
|
||||
self::IGNORED_PROPERTIES
|
||||
);
|
||||
|
||||
// substr of calendars/ => path is inside the CalDAV component
|
||||
|
|
@ -128,8 +141,12 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
$props = $this->getProperties($path, $requestedProps);
|
||||
foreach ($props as $propName => $propValue) {
|
||||
// First fetch the published properties (set by another user), then get the ones set by
|
||||
// the current user. If both are set then the latter as priority.
|
||||
foreach ($this->getPublishedProperties($path, $requestedProps) as $propName => $propValue) {
|
||||
$propFind->set($propName, $propValue);
|
||||
}
|
||||
foreach ($this->getUserProperties($path, $requestedProps) as $propName => $propValue) {
|
||||
$propFind->set($propName, $propValue);
|
||||
}
|
||||
}
|
||||
|
|
@ -160,7 +177,7 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
$statement->execute([$this->user->getUID(), $this->formatPath($path)]);
|
||||
$statement->closeCursor();
|
||||
|
||||
unset($this->cache[$path]);
|
||||
unset($this->userCache[$path]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -181,7 +198,33 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of properties for this nodes.;
|
||||
* @param string $path
|
||||
* @param string[] $requestedProperties
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getPublishedProperties(string $path, array $requestedProperties): array {
|
||||
$allowedProps = array_intersect(self::PUBLISHED_READ_ONLY_PROPERTIES, $requestedProperties);
|
||||
|
||||
if (empty($allowedProps)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$qb->select('*')
|
||||
->from(self::TABLE_NAME)
|
||||
->where($qb->expr()->eq('propertypath', $qb->createNamedParameter($path)));
|
||||
$result = $qb->executeQuery();
|
||||
$props = [];
|
||||
while ($row = $result->fetch()) {
|
||||
$props[$row['propertyname']] = $row['propertyvalue'];
|
||||
}
|
||||
$result->closeCursor();
|
||||
return $props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of properties for the given path and current user
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $requestedProperties requested properties or empty array for "all"
|
||||
|
|
@ -191,9 +234,9 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
* http://www.example.org/namespace#author If the array is empty, all
|
||||
* properties should be returned
|
||||
*/
|
||||
private function getProperties(string $path, array $requestedProperties) {
|
||||
if (isset($this->cache[$path])) {
|
||||
return $this->cache[$path];
|
||||
private function getUserProperties(string $path, array $requestedProperties) {
|
||||
if (isset($this->userCache[$path])) {
|
||||
return $this->userCache[$path];
|
||||
}
|
||||
|
||||
// TODO: chunking if more than 1000 properties
|
||||
|
|
@ -222,7 +265,7 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
|
||||
$result->closeCursor();
|
||||
|
||||
$this->cache[$path] = $props;
|
||||
$this->userCache[$path] = $props;
|
||||
return $props;
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +288,7 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
|
||||
|
||||
// TODO: use "insert or update" strategy ?
|
||||
$existing = $this->getProperties($path, []);
|
||||
$existing = $this->getUserProperties($path, []);
|
||||
$this->connection->beginTransaction();
|
||||
foreach ($properties as $propertyName => $propertyValue) {
|
||||
// If it was null, we need to delete the property
|
||||
|
|
@ -283,7 +326,7 @@ class CustomPropertiesBackend implements BackendInterface {
|
|||
}
|
||||
|
||||
$this->connection->commit();
|
||||
unset($this->cache[$path]);
|
||||
unset($this->userCache[$path]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue