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

CalDavBackend, implement SharingSupport interface. #36766

Closed
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
113 changes: 102 additions & 11 deletions apps/dav/lib/CalDAV/CalDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@
use Sabre\CalDAV\Backend\SchedulingSupport;
use Sabre\CalDAV\Backend\SubscriptionSupport;
use Sabre\CalDAV\Backend\SyncSupport;
use Sabre\CalDAV\Backend\SharingSupport;
use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
use Sabre\DAV;
use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\PropPatch;
use Sabre\DAV\Xml\Element\Sharee;
use Sabre\Uri;
use Sabre\VObject\Component;
use Sabre\VObject\Component\VCalendar;
Expand Down Expand Up @@ -118,7 +120,7 @@
*
* @package OCA\DAV\CalDAV
*/
class CalDavBackend extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport {
class CalDavBackend extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport, SharingSupport {
use TTransactional;

public const CALENDAR_TYPE_CALENDAR = 0;
Expand Down Expand Up @@ -362,6 +364,9 @@ public function getCalendarsForUser($principalUri) {
$calendar = $this->addOwnerPrincipalToCalendar($calendar);
$calendar = $this->addResourceTypeToCalendar($row, $calendar);

$calendar['{DAV:}share-resource-uri'] = '/ns/share/' . $calendar['id'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: Not a fan of this. The PDO backend does the exact same, but is this form even a valid URI?

$calendar['{DAV:}share-access'] = \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER;

if (!isset($calendars[$calendar['id']])) {
$calendars[$calendar['id']] = $calendar;
}
Expand Down Expand Up @@ -436,6 +441,9 @@ public function getCalendarsForUser($principalUri) {
$calendar = $this->addOwnerPrincipalToCalendar($calendar);
$calendar = $this->addResourceTypeToCalendar($row, $calendar);

$calendar['{DAV:}share-resource-uri'] = '/ns/share/' . $calendar['id'];
$calendar['{DAV:}share-access'] = $row['access'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (blocking): The access values we use are (see ACCESS_PUBLIC in this file and ACCESS_* in OCA\DAV\DAV\Sharing\Backend) different from the ones declared Sabre\DAV\Sharing\Plugin. You need to convert them.


$calendars[$calendar['id']] = $calendar;
}
$result->closeCursor();
Expand Down Expand Up @@ -658,7 +666,7 @@ public function getCalendarByUri($principal, $uri) {
}

/**
* @return array{id: int, uri: string, '{http://calendarserver.org/ns/}getctag': string, '{http://sabredav.org/ns}sync-token': int, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set': SupportedCalendarComponentSet, '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp': ScheduleCalendarTransp, '{urn:ietf:params:xml:ns:caldav}calendar-timezone': ?string }|null
* @return array{id: int, uri: string, principaluri: string, '{http://calendarserver.org/ns/}getctag': string, '{http://sabredav.org/ns}sync-token': int, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set': SupportedCalendarComponentSet, '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp': ScheduleCalendarTransp, '{urn:ietf:params:xml:ns:caldav}calendar-timezone': ?string }|null
*/
public function getCalendarById(int $calendarId): ?array {
$fields = array_column($this->propertyMap, 0);
Expand Down Expand Up @@ -2823,23 +2831,28 @@ public function getShares(int $resourceId): array {
}

/**
* @param boolean $value
* @param \OCA\DAV\CalDAV\Calendar $calendar
* @return string|null
*/
public function setPublishStatus($value, $calendar) {
$calendarId = $calendar->getResourceId();
* Publishes a calendar.
*
* @param mixed $calendarId
* @param bool $value
* @return null|string
*/
public function setPublishStatus($calendarId, $value)
{
$calendarData = $this->getCalendarById($calendarId);
if ($calendarData === null) {
return null;
}

$query = $this->db->getQueryBuilder();
if ($value) {
$publicUri = $this->random->generate(16, ISecureRandom::CHAR_HUMAN_READABLE);
$query->insert('dav_shares')
->values([
'principaluri' => $query->createNamedParameter($calendar->getPrincipalURI()),
'principaluri' => $query->createNamedParameter($calendarData['principaluri']),
'type' => $query->createNamedParameter('calendar'),
'access' => $query->createNamedParameter(self::ACCESS_PUBLIC),
'resourceid' => $query->createNamedParameter($calendar->getResourceId()),
'resourceid' => $query->createNamedParameter($calendarId),
'publicuri' => $query->createNamedParameter($publicUri)
]);
$query->executeStatement();
Expand All @@ -2848,7 +2861,7 @@ public function setPublishStatus($value, $calendar) {
return $publicUri;
}
$query->delete('dav_shares')
->where($query->expr()->eq('resourceid', $query->createNamedParameter($calendar->getResourceId())))
->where($query->expr()->eq('resourceid', $query->createNamedParameter($calendarId)))
->andWhere($query->expr()->eq('access', $query->createNamedParameter(self::ACCESS_PUBLIC)));
$query->executeStatement();

Expand Down Expand Up @@ -2882,6 +2895,84 @@ public function applyShareAcl(int $resourceId, array $acl): array {
return $this->calendarSharingBackend->applyShareAcl($resourceId, $acl);
}

/**
* Updates the list of shares.
*
* @param mixed $calendarId
* @param \Sabre\DAV\Xml\Element\Sharee[] $sharees
* @return void
*/
public function updateInvites($calendarId, array $sharees) {
$currentShares = $this->getShares($calendarId);

$removals = [];
$additions = [];

foreach ($sharees as $sharee) {
if (\Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS === $sharee->access) {
// if access was set no NOACCESS, it means access for an
// existing sharee was removed.
$removals[] = $sharee->href;
continue;
}

if (is_null($sharee->principal)) {
// If the server could not determine the principal automatically,
// we will mark the invite status as invalid.
continue;
}

$additions[] = [
'href' => $sharee->href,
'commonName' => $sharee->properties['{DAV:}displayname'] ?? '',
'readOnly' => $sharee->access == \Sabre\DAV\Sharing\Plugin::ACCESS_READ,
];
}
// updateShares() needs a IShareable, i.e. a Calendar object. This is
// really hacky now ... and inefficient ...
Comment on lines +2931 to +2932
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This restriction and the whole current implementation of updateShares & getShares is oriented to match the current OCA\DAV\DAV\Sharing\Plugin way of handling shares.

Sharing addressbooks would be the only reason to keep it if we have this instead, so if we manage to overcome that, we could refactor the whole thing.

$calendarInfo = $this->getCalendarById($calendarId);
if ($calendarInfo === null) {
return;
}
$principalUri = $calendarInfo['principaluri'];
$calendarUri = $calendarInfo['uri'];
$shareable = new Calendar($this, $calendarInfo, \OCP\Util::getL10N('dav'), $this->config, $this->logger);
$this->updateShares($shareable, $additions, $removals);

Check notice

Code scanning / Psalm

ArgumentTypeCoercion

Argument 2 of OCA\DAV\CalDAV\CalDavBackend::updateShares expects list<array{commonName: string, href: string, readOnly: bool}>, but parent type list<array{commonName: ""|mixed, href: string, readOnly: bool}> provided
}

/**
* Returns the list of people whom this calendar is shared with.
*
* Every item in the returned list must be a Sharee object with at
* least the following properties set:
* $href
* $shareAccess
* $inviteStatus
*
* and optionally:
* $properties
*
* @param mixed $calendarId
*
* @return \Sabre\DAV\Xml\Element\Sharee[]
*/
public function getInvites($calendarId) {
$shares = $this->getShares($calendarId);
$result = [];
foreach ($shares as $share) {
$result[] = new Sharee([
'href' => $share['href'],
'access' => $share['readOnly'] ? \Sabre\DAV\Sharing\Plugin::ACCESS_READ : \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE,
'inviteStatus' => \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: This makes me want invite support. :D

'properties' => !empty($share['commonName'])
? [ '{DAV:}displayname' => $share['commonName'] ]
: [],
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
]);
}
return $result;
}

/**
* update properties table
*
Expand Down
2 changes: 1 addition & 1 deletion apps/dav/lib/CalDAV/Calendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ public function calendarQuery(array $filters) {
* @return string|null
*/
public function setPublishStatus($value) {
$publicUri = $this->caldavBackend->setPublishStatus($value, $this);
$publicUri = $this->caldavBackend->setPublishStatus($this->getResourceId(), $value);
$this->calendarInfo['publicuri'] = $publicUri;
return $publicUri;
}
Expand Down