From 7c3e231ee6c124860ebbaa736b62768576825771 Mon Sep 17 00:00:00 2001 From: Trekky12 Date: Sat, 8 Feb 2025 11:02:49 +0100 Subject: [PATCH] Fix showing owner of shared calendars on iOS The iOS calendar uses the {DAV:}owner attribute to determine the owner of a shared calendar. Therefore this attribute needs to be set as the real owner of the calendar not the user which whom the calendar is shared. This "real" owner needs to be set before the ACL plugin can set it. To determine the name of the owner the iOS calendar queries the principal uri. Setting a mailto address is not working. Unfortunatly the principal query is not allowed for other users by the ACL plugin. It is not easy to determine if the client is trying to query the principal information of a owner of a shared calendar, so as workaround all authenticated users are allowed to query the principal information. --- lib/CalDAV/Backend/PDO.php | 10 +++++++--- lib/CalDAV/Principal/User.php | 16 ++++++++++++++++ lib/CalDAV/SharedCalendar.php | 12 ++++++++++++ lib/CalDAV/SharingPlugin.php | 10 +++++++++- lib/DAV/Sharing/ISharedNode.php | 9 +++++++++ 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/lib/CalDAV/Backend/PDO.php b/lib/CalDAV/Backend/PDO.php index 91b5da4411..b6d59b1690 100644 --- a/lib/CalDAV/Backend/PDO.php +++ b/lib/CalDAV/Backend/PDO.php @@ -192,9 +192,13 @@ public function getCalendarsForUser($principalUri) $calendar['share-access'] = (int) $row['access']; // 1 = owner, 2 = readonly, 3 = readwrite if ($row['access'] > 1) { - // We need to find more information about the original owner. - //$stmt2 = $this->pdo->prepare('SELECT principaluri FROM ' . $this->calendarInstancesTableName . ' WHERE access = 1 AND id = ?'); - //$stmt2->execute([$row['id']]); + $ownerStmt = $this->pdo->prepare("SELECT principaluri FROM {$this->calendarInstancesTableName} WHERE access = 1 AND calendarid = ?"); + $ownerStmt->execute([$row['calendarid']]); + + $ownerRow = $ownerStmt->fetch(\PDO::FETCH_ASSOC); + if ($ownerRow && is_array($ownerRow) && array_key_exists('principaluri', $ownerRow)) { + $calendar['owner-principal'] = $ownerRow['principaluri']; + } // read-only is for backwards compatibility. Might go away in // the future. diff --git a/lib/CalDAV/Principal/User.php b/lib/CalDAV/Principal/User.php index 88bf4b4f77..3973d56648 100644 --- a/lib/CalDAV/Principal/User.php +++ b/lib/CalDAV/Principal/User.php @@ -131,6 +131,22 @@ public function getACL() 'protected' => true, ]; + /** + * Members of shared calendars needs to be able to read information about the owner. + * + * The Principal has no knowledge about the calendars and therefore it is not + * possible to limit the access to members of a shared calendar + * in DAVACL/Plugin.php getCurrentUserPrivilegeSet. + * + * As workaround all authenticated users are getting the read privilege for other users. + */ + + $acl[] = [ + 'privilege' => '{DAV:}read', + 'principal' => '{DAV:}authenticated', + 'protected' => true, + ]; + return $acl; } } diff --git a/lib/CalDAV/SharedCalendar.php b/lib/CalDAV/SharedCalendar.php index 818392f57c..cd1eefbe92 100644 --- a/lib/CalDAV/SharedCalendar.php +++ b/lib/CalDAV/SharedCalendar.php @@ -216,4 +216,16 @@ public function getChildACL() return $acl; } + + /** + * Returns the 'original owner principal' for this shared resource. + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwnerPrincipal() + { + return isset($this->calendarInfo['owner-principal']) ? $this->calendarInfo['owner-principal'] : $this->getOwner(); + } } diff --git a/lib/CalDAV/SharingPlugin.php b/lib/CalDAV/SharingPlugin.php index 56962fc14f..cc0bbf8f62 100644 --- a/lib/CalDAV/SharingPlugin.php +++ b/lib/CalDAV/SharingPlugin.php @@ -86,7 +86,7 @@ public function initialize(DAV\Server $server) $this->server->xml->elementMap['{'.Plugin::NS_CALENDARSERVER.'}share'] = \Sabre\CalDAV\Xml\Request\Share::class; $this->server->xml->elementMap['{'.Plugin::NS_CALENDARSERVER.'}invite-reply'] = \Sabre\CalDAV\Xml\Request\InviteReply::class; - $this->server->on('propFind', [$this, 'propFindEarly']); + $this->server->on('propFind', [$this, 'propFindEarly'], 10); $this->server->on('propFind', [$this, 'propFindLate'], 150); $this->server->on('propPatch', [$this, 'propPatch'], 40); $this->server->on('method:POST', [$this, 'httpPost']); @@ -106,6 +106,14 @@ public function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) $node->getInvites() ); }); + + // Needs to be called before ACL + $propFind->handle('{DAV:}owner', function () use ($node) { + $shareAccess = $node->getShareAccess(); + if ($shareAccess > 1) { + return new \Sabre\DAV\Xml\Property\Href($node->getOwnerPrincipal() . '/'); + } + }); } } diff --git a/lib/DAV/Sharing/ISharedNode.php b/lib/DAV/Sharing/ISharedNode.php index a746ac7535..9d1e261513 100644 --- a/lib/DAV/Sharing/ISharedNode.php +++ b/lib/DAV/Sharing/ISharedNode.php @@ -66,4 +66,13 @@ public function updateInvites(array $sharees); * @return \Sabre\DAV\Xml\Element\Sharee[] */ public function getInvites(); + + /** + * Returns the 'original owner principal' for this shared resource. + * + * This must be a url to a principal, or null if there's no owner + * + * @return string|null + */ + public function getOwnerPrincipal(); }