]> source.dussan.org Git - nextcloud-server.git/commitdiff
Filter confidential calendar objects in shared calendars
authorThomas Müller <thomas.mueller@tmit.eu>
Tue, 31 May 2016 16:10:31 +0000 (18:10 +0200)
committerThomas Müller <thomas.mueller@tmit.eu>
Thu, 9 Jun 2016 09:09:14 +0000 (11:09 +0200)
Filter private calendar objects in shared calendars

.htaccess
apps/dav/lib/CalDAV/CalDavBackend.php
apps/dav/lib/CalDAV/Calendar.php
apps/dav/lib/CalDAV/CalendarObject.php [new file with mode: 0644]
apps/dav/tests/unit/CalDAV/CalendarTest.php

index 5628af585df842134cce752b83e5861ea598ba01..bd9f5af3f707a93df10b4b08ce31e0aa30b8c83d 100644 (file)
--- a/.htaccess
+++ b/.htaccess
@@ -72,7 +72,3 @@ Options -Indexes
 <IfModule pagespeed_module>
   ModPagespeed Off
 </IfModule>
-#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
-
-ErrorDocument 403 /core/templates/403.php
-ErrorDocument 404 /core/templates/404.php
index 1950b87df34a754821070644807604bb1e8f760e..ce4940829761d2615a7767d5927e318a9a93700b 100644 (file)
@@ -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']
                        ];
 
                }
index 73b3957a9b063a9b31988b4ad178e9aab8471edf..785bb5699e2418bf40d13f50537dcec2bef03ed1 100644 (file)
@@ -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 (file)
index 0000000..b4a58b5
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+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();
+       }
+
+}
index 73d85e82bbcefae7122456e1128518ca0131e5d4..56a2d4fcba76e1f6fd0349805edfdcfe437ba3db 100644 (file)
@@ -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 = <<<EOD
+BEGIN:VCALENDAR
+PRODID:-//ownCloud calendar v1.2.2
+BEGIN:VEVENT
+CREATED:20160602T133732
+DTSTAMP:20160602T133732
+LAST-MODIFIED:20160602T133732
+UID:wej2z68l9h
+SUMMARY:Test Event
+LOCATION:Somewhere ...
+ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CUTYPE=INDIVIDUAL;CN=de
+ epdiver:MAILTO:thomas.mueller@tmit.eu
+ORGANIZER;CN=deepdiver:MAILTO:thomas.mueller@tmit.eu
+DESCRIPTION:maybe ....
+DTSTART;TZID=Europe/Berlin;VALUE=DATE:$start
+DTEND;TZID=Europe/Berlin;VALUE=DATE:$end
+RRULE:FREQ=DAILY
+BEGIN:VALARM
+ACTION:AUDIO
+TRIGGER:-PT15M
+END:VALARM
+END:VEVENT
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+BEGIN:DAYLIGHT
+DTSTART:19810329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+TZNAME:MESZ
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:19961027T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+TZNAME:MEZ
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+END:STANDARD
+END:VTIMEZONE
+END:VCALENDAR
+EOD;
+
+               $calObject0 = ['uri' => '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]
+               ];
+       }
 }