From bfcd1dc49c6fe296559131841ee0096ec1ce89c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 31 May 2016 18:10:31 +0200 Subject: [PATCH] Filter confidential calendar objects in shared calendars Filter private calendar objects in shared calendars --- .htaccess | 4 - apps/dav/lib/CalDAV/CalDavBackend.php | 6 +- apps/dav/lib/CalDAV/Calendar.php | 77 ++++++++++ apps/dav/lib/CalDAV/CalendarObject.php | 92 ++++++++++++ apps/dav/tests/unit/CalDAV/CalendarTest.php | 150 ++++++++++++++++++++ 5 files changed, 322 insertions(+), 7 deletions(-) create mode 100644 apps/dav/lib/CalDAV/CalendarObject.php diff --git a/.htaccess b/.htaccess index 5628af585d..bd9f5af3f7 100644 --- a/.htaccess +++ b/.htaccess @@ -72,7 +72,3 @@ Options -Indexes ModPagespeed Off -#### DO NOT CHANGE ANYTHING ABOVE THIS LINE #### - -ErrorDocument 403 /core/templates/403.php -ErrorDocument 404 /core/templates/404.php diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 1950b87df3..ce49408297 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -504,7 +504,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'component' => strtolower($row['componenttype']), - 'classification'=> $row['classification'] + 'classification'=> (int)$row['classification'] ]; } @@ -548,7 +548,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription 'size' => (int)$row['size'], 'calendardata' => $this->readBlob($row['calendardata']), 'component' => strtolower($row['componenttype']), - 'classification'=> $row['classification'] + 'classification'=> (int)$row['classification'] ]; } @@ -586,7 +586,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription 'size' => (int)$row['size'], 'calendardata' => $this->readBlob($row['calendardata']), 'component' => strtolower($row['componenttype']), - 'classification' => $row['classification'] + 'classification' => (int)$row['classification'] ]; } diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php index 73b3957a9b..785bb5699e 100644 --- a/apps/dav/lib/CalDAV/Calendar.php +++ b/apps/dav/lib/CalDAV/Calendar.php @@ -26,6 +26,7 @@ use OCA\DAV\DAV\Sharing\IShareable; use OCP\IL10N; use Sabre\CalDAV\Backend\BackendInterface; use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropPatch; class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { @@ -162,6 +163,78 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { parent::propPatch($propPatch); } + function getChild($name) { + + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); + + if (!$obj) { + throw new NotFound('Calendar object not found'); + } + + if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + throw new NotFound('Calendar object not found'); + } + + $obj['acl'] = $this->getChildACL(); + + return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + + } + + function getChildren() { + + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = []; + foreach ($objs as $obj) { + if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + continue; + } + $obj['acl'] = $this->getChildACL(); + $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + } + return $children; + + } + + function getMultipleChildren(array $paths) { + + $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths); + $children = []; + foreach ($objs as $obj) { + if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + continue; + } + $obj['acl'] = $this->getChildACL(); + $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj); + } + return $children; + + } + + function childExists($name) { + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); + if (!$obj) { + return false; + } + if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + return false; + } + + return true; + } + + function calendarQuery(array $filters) { + + $uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); + if ($this->isShared()) { + return array_filter($uris, function ($uri) { + return $this->childExists($uri); + }); + } + + return $uris; + } + private function canWrite() { if (isset($this->calendarInfo['{http://owncloud.org/ns}read-only'])) { return !$this->calendarInfo['{http://owncloud.org/ns}read-only']; @@ -169,4 +242,8 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { return true; } + private function isShared() { + return isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']); + } + } diff --git a/apps/dav/lib/CalDAV/CalendarObject.php b/apps/dav/lib/CalDAV/CalendarObject.php new file mode 100644 index 0000000000..b4a58b5209 --- /dev/null +++ b/apps/dav/lib/CalDAV/CalendarObject.php @@ -0,0 +1,92 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + + +namespace OCA\DAV\CalDAV; + + +use Sabre\VObject\Component; +use Sabre\VObject\Property; +use Sabre\VObject\Reader; + +class CalendarObject extends \Sabre\CalDAV\CalendarObject { + + /** + * @inheritdoc + */ + function get() { + $data = parent::get(); + if ($this->isShared() && $this->objectData['classification'] === CalDavBackend::CLASSIFICATION_CONFIDENTIAL) { + return $this->createConfidentialObject($data); + } + return $data; + } + + private function isShared() { + return isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']); + } + + /** + * @param string $calData + * @return string + */ + private static function createConfidentialObject($calData) { + + $vObject = Reader::read($calData); + + /** @var Component $vElement */ + $vElement = null; + if(isset($vObject->VEVENT)) { + $vElement = $vObject->VEVENT; + } + if(isset($vObject->VJOURNAL)) { + $vElement = $vObject->VJOURNAL; + } + if(isset($vObject->VTODO)) { + $vElement = $vObject->VTODO; + } + if(!is_null($vElement)) { + foreach ($vElement->children as &$property) { + /** @var Property $property */ + switch($property->name) { + case 'CREATED': + case 'DTSTART': + case 'RRULE': + case 'DURATION': + case 'DTEND': + case 'CLASS': + case 'UID': + break; + case 'SUMMARY': + $property->setValue('Busy'); + break; + default: + $vElement->__unset($property->name); + unset($property); + break; + } + } + } + + return $vObject->serialize(); + } + +} diff --git a/apps/dav/tests/unit/CalDAV/CalendarTest.php b/apps/dav/tests/unit/CalDAV/CalendarTest.php index 73d85e82bb..56a2d4fcba 100644 --- a/apps/dav/tests/unit/CalDAV/CalendarTest.php +++ b/apps/dav/tests/unit/CalDAV/CalendarTest.php @@ -27,6 +27,7 @@ use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\Calendar; use OCP\IL10N; use Sabre\DAV\PropPatch; +use Sabre\VObject\Reader; use Test\TestCase; class CalendarTest extends TestCase { @@ -189,4 +190,153 @@ class CalendarTest extends TestCase { 'birthday calendar' => [false, false, false, BirthdayService::BIRTHDAY_CALENDAR_URI] ]; } + + /** + * @dataProvider providesConfidentialClassificationData + * @param $expectedChildren + * @param $isShared + */ + public function testPrivateClassification($expectedChildren, $isShared) { + + $calObject0 = ['uri' => 'event-0', 'classification' => CalDavBackend::CLASSIFICATION_PUBLIC]; + $calObject1 = ['uri' => 'event-1', 'classification' => CalDavBackend::CLASSIFICATION_CONFIDENTIAL]; + $calObject2 = ['uri' => 'event-2', 'classification' => CalDavBackend::CLASSIFICATION_PRIVATE]; + + /** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */ + $backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock(); + $backend->expects($this->any())->method('getCalendarObjects')->willReturn([ + $calObject0, $calObject1, $calObject2 + ]); + $backend->expects($this->any())->method('getMultipleCalendarObjects') + ->with(666, ['event-0', 'event-1', 'event-2']) + ->willReturn([ + $calObject0, $calObject1, $calObject2 + ]); + $backend->expects($this->any())->method('getCalendarObject') + ->willReturn($calObject2)->with(666, 'event-2'); + + $calendarInfo = [ + 'principaluri' => 'user2', + 'id' => 666, + 'uri' => 'cal', + ]; + + if ($isShared) { + $calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1'; + + } + $c = new Calendar($backend, $calendarInfo, $this->l10n); + $children = $c->getChildren(); + $this->assertEquals($expectedChildren, count($children)); + $children = $c->getMultipleChildren(['event-0', 'event-1', 'event-2']); + $this->assertEquals($expectedChildren, count($children)); + + $this->assertEquals(!$isShared, $c->childExists('event-2')); + } + + /** + * @dataProvider providesConfidentialClassificationData + * @param $expectedChildren + * @param $isShared + */ + public function testConfidentialClassification($expectedChildren, $isShared) { + $start = '20160609'; + $end = '20160610'; + + $calData = << 'event-0', 'classification' => CalDavBackend::CLASSIFICATION_PUBLIC]; + $calObject1 = ['uri' => 'event-1', 'classification' => CalDavBackend::CLASSIFICATION_CONFIDENTIAL, 'calendardata' => $calData]; + $calObject2 = ['uri' => 'event-2', 'classification' => CalDavBackend::CLASSIFICATION_PRIVATE]; + + /** @var \PHPUnit_Framework_MockObject_MockObject | CalDavBackend $backend */ + $backend = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend')->disableOriginalConstructor()->getMock(); + $backend->expects($this->any())->method('getCalendarObjects')->willReturn([ + $calObject0, $calObject1, $calObject2 + ]); + $backend->expects($this->any())->method('getMultipleCalendarObjects') + ->with(666, ['event-0', 'event-1', 'event-2']) + ->willReturn([ + $calObject0, $calObject1, $calObject2 + ]); + $backend->expects($this->any())->method('getCalendarObject') + ->willReturn($calObject1)->with(666, 'event-1'); + + $calendarInfo = [ + 'principaluri' => 'user2', + 'id' => 666, + 'uri' => 'cal', + ]; + + if ($isShared) { + $calendarInfo['{http://owncloud.org/ns}owner-principal'] = 'user1'; + + } + $c = new Calendar($backend, $calendarInfo, $this->l10n); + + // test private event + $privateEvent = $c->getChild('event-1'); + $calData = $privateEvent->get(); + $event = Reader::read($calData); + + $this->assertEquals($start, $event->VEVENT->DTSTART->getValue()); + $this->assertEquals($end, $event->VEVENT->DTEND->getValue()); + + if ($isShared) { + $this->assertEquals('Busy', $event->VEVENT->SUMMARY->getValue()); + $this->assertArrayNotHasKey('ATTENDEE', $event->VEVENT); + $this->assertArrayNotHasKey('LOCATION', $event->VEVENT); + $this->assertArrayNotHasKey('DESCRIPTION', $event->VEVENT); + $this->assertArrayNotHasKey('ORGANIZER', $event->VEVENT); + } else { + $this->assertEquals('Test Event', $event->VEVENT->SUMMARY->getValue()); + } + } + + public function providesConfidentialClassificationData() { + return [ + [3, false], + [2, true] + ]; + } }