Merge pull request #21895 from owncloud/calendar-sharing

Migrate from CardDAV sharing to more general DAV sharing
remotes/origin/comments-markallread-dav
Thomas Müller 2016-01-27 19:32:05 +07:00
commit 0f51851d7d
12 changed files with 912 additions and 132 deletions

@ -20,10 +20,10 @@
*/
namespace OCA\DAV\CardDAV;
use OCA\DAV\CardDAV\Sharing\IShareableAddressBook;
use OCA\DAV\DAV\Sharing\IShareable;
use Sabre\DAV\Exception\NotFound;
class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddressBook {
class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable {
public function __construct(CardDavBackend $carddavBackend, array $addressBookInfo) {
parent::__construct($carddavBackend, $addressBookInfo);
@ -68,7 +68,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddres
function getShares() {
/** @var CardDavBackend $carddavBackend */
$carddavBackend = $this->carddavBackend;
return $carddavBackend->getShares($this->getBookId());
return $carddavBackend->getShares($this->getResourceId());
}
function getACL() {
@ -82,14 +82,14 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddres
}
// add the current user
if (isset($this->addressBookInfo['{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal'])) {
$owner = $this->addressBookInfo['{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal'];
if (isset($this->addressBookInfo['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal'])) {
$owner = $this->addressBookInfo['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal'];
$acl[] = [
'privilege' => '{DAV:}read',
'principal' => $owner,
'protected' => true,
];
if ($this->addressBookInfo['{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only']) {
if ($this->addressBookInfo['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only']) {
$acl[] = [
'privilege' => '{DAV:}write',
'principal' => $owner,
@ -100,7 +100,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddres
/** @var CardDavBackend $carddavBackend */
$carddavBackend = $this->carddavBackend;
return $carddavBackend->applyShareAcl($this->getBookId(), $acl);
return $carddavBackend->applyShareAcl($this->getResourceId(), $acl);
}
function getChildACL() {
@ -115,11 +115,11 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddres
/** @var CardDavBackend $carddavBackend */
$carddavBackend = $this->carddavBackend;
return $carddavBackend->applyShareAcl($this->getBookId(), $acl);
return $carddavBackend->applyShareAcl($this->getResourceId(), $acl);
}
function getChild($name) {
$obj = $this->carddavBackend->getCard($this->getBookId(), $name);
$obj = $this->carddavBackend->getCard($this->getResourceId(), $name);
if (!$obj) {
throw new NotFound('Card not found');
}
@ -129,8 +129,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddres
/**
* @return int
*/
public function getBookId() {
public function getResourceId() {
return $this->addressBookInfo['id'];
}
}

@ -24,9 +24,10 @@
namespace OCA\DAV\CardDAV;
use Doctrine\DBAL\Connection;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCA\DAV\DAV\Sharing\Backend;
use OCA\DAV\DAV\Sharing\IShareable;
use OCP\IDBConnection;
use Sabre\CardDAV\Backend\BackendInterface;
use Sabre\CardDAV\Backend\SyncSupport;
@ -50,6 +51,9 @@ class CardDavBackend implements BackendInterface, SyncSupport {
/** @var IDBConnection */
private $db;
/** @var Backend */
private $sharingBackend;
/** @var array properties to index */
public static $indexProperties = array(
'BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME',
@ -68,6 +72,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
public function __construct(IDBConnection $db, Principal $principalBackend) {
$this->db = $db;
$this->principalBackend = $principalBackend;
$this->sharingBackend = new Backend($this->db, 'addressbook');
}
/**
@ -136,8 +141,8 @@ class CardDavBackend implements BackendInterface, SyncSupport {
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
'{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
'{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'],
'{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => $row['access'] === self::ACCESS_READ,
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'],
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only' => $row['access'] === self::ACCESS_READ,
];
}
$result->closeCursor();
@ -715,17 +720,12 @@ class CardDavBackend implements BackendInterface, SyncSupport {
}
/**
* @param AddressBook $book
* @param IShareable $shareable
* @param string[] $add
* @param string[] $remove
*/
public function updateShares($book, $add, $remove) {
foreach($add as $element) {
$this->shareWith($book, $element);
}
foreach($remove as $element) {
$this->unshare($book->getBookId(), $element);
}
public function updateShares(IShareable $shareable, $add, $remove) {
$this->sharingBackend->updateShares($shareable, $add, $remove);
}
/**
@ -808,63 +808,6 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return $result;
}
/**
* @param AddressBook $addressBook
* @param string $element
*/
private function shareWith($addressBook, $element) {
$user = $element['href'];
$parts = explode(':', $user, 2);
if ($parts[0] !== 'principal') {
return;
}
$p = $this->principalBackend->getPrincipalByPath($parts[1]);
if (is_null($p)) {
return;
}
// remove the share if it already exists
$this->unshare($addressBook->getBookId(), $element['href']);
$access = self::ACCESS_READ;
if (isset($element['readOnly'])) {
$access = $element['readOnly'] ? self::ACCESS_READ : self::ACCESS_READ_WRITE;
}
$query = $this->db->getQueryBuilder();
$query->insert('dav_shares')
->values([
'principaluri' => $query->createNamedParameter($parts[1]),
'type' => $query->createNamedParameter('addressbook'),
'access' => $query->createNamedParameter($access),
'resourceid' => $query->createNamedParameter($addressBook->getBookId())
]);
$query->execute();
}
/**
* @param int $addressBookId
* @param string $element
*/
private function unshare($addressBookId, $element) {
$parts = explode(':', $element, 2);
if ($parts[0] !== 'principal') {
return;
}
$p = $this->principalBackend->getPrincipalByPath($parts[1]);
if (is_null($p)) {
return;
}
$query = $this->db->getQueryBuilder();
$query->delete('dav_shares')
->where($query->expr()->eq('resourceid', $query->createNamedParameter($addressBookId)))
->andWhere($query->expr()->eq('type', $query->createNamedParameter('addressbook')))
->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($parts[1])))
;
$query->execute();
}
/**
* Returns the list of people whom this address book is shared with.
*
@ -878,26 +821,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
* @return array
*/
public function getShares($addressBookId) {
$query = $this->db->getQueryBuilder();
$result = $query->select(['principaluri', 'access'])
->from('dav_shares')
->where($query->expr()->eq('resourceid', $query->createNamedParameter($addressBookId)))
->andWhere($query->expr()->eq('type', $query->createNamedParameter('addressbook')))
->execute();
$shares = [];
while($row = $result->fetch()) {
$p = $this->principalBackend->getPrincipalByPath($row['principaluri']);
$shares[]= [
'href' => "principal:${p['uri']}",
'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '',
'status' => 1,
'readOnly' => ($row['access'] === self::ACCESS_READ),
'{'.\OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD.'}principal' => $p['uri']
];
}
return $shares;
return $this->sharingBackend->getShares($addressBookId);
}
/**
@ -1001,13 +925,13 @@ class CardDavBackend implements BackendInterface, SyncSupport {
foreach ($shares as $share) {
$acl[] = [
'privilege' => '{DAV:}read',
'principal' => $share['{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
'protected' => true,
];
if (!$share['readOnly']) {
$acl[] = [
'privilege' => '{DAV:}write',
'principal' => $share['{' . \OCA\DAV\CardDAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
'protected' => true,
];
}

@ -0,0 +1,173 @@
<?php
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
* @author Björn Schießle <schiessle@owncloud.com>
* @author Scrutinizer Auto-Fixer <auto-fixer@scrutinizer-ci.com>
* @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\DAV\Sharing;
use OCP\IDBConnection;
class Backend {
/** @var IDBConnection */
private $db;
const ACCESS_OWNER = 1;
const ACCESS_READ_WRITE = 2;
const ACCESS_READ = 3;
/** @var string */
private $resourceType;
/**
* CardDavBackend constructor.
*
* @param IDBConnection $db
*/
public function __construct(IDBConnection $db, $resourceType) {
$this->db = $db;
$this->resourceType = $resourceType;
}
/**
* @param IShareable $shareable
* @param string[] $add
* @param string[] $remove
*/
public function updateShares($shareable, $add, $remove) {
foreach($add as $element) {
$this->shareWith($shareable, $element);
}
foreach($remove as $element) {
$this->unshare($shareable->getResourceId(), $element);
}
}
/**
* @param IShareable $shareable
* @param string $element
*/
private function shareWith($shareable, $element) {
$user = $element['href'];
$parts = explode(':', $user, 2);
if ($parts[0] !== 'principal') {
return;
}
// remove the share if it already exists
$this->unshare($shareable->getResourceId(), $element['href']);
$access = self::ACCESS_READ;
if (isset($element['readOnly'])) {
$access = $element['readOnly'] ? self::ACCESS_READ : self::ACCESS_READ_WRITE;
}
$query = $this->db->getQueryBuilder();
$query->insert('dav_shares')
->values([
'principaluri' => $query->createNamedParameter($parts[1]),
'type' => $query->createNamedParameter($this->resourceType),
'access' => $query->createNamedParameter($access),
'resourceid' => $query->createNamedParameter($shareable->getResourceId())
]);
$query->execute();
}
/**
* @param int $resourceId
* @param string $element
*/
private function unshare($resourceId, $element) {
$parts = explode(':', $element, 2);
if ($parts[0] !== 'principal') {
return;
}
$query = $this->db->getQueryBuilder();
$query->delete('dav_shares')
->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($parts[1])))
;
$query->execute();
}
/**
* Returns the list of people whom this resource is shared with.
*
* Every element in this array should have the following properties:
* * href - Often a mailto: address
* * commonName - Optional, for example a first + last name
* * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
* * readOnly - boolean
* * summary - Optional, a description for the share
*
* @return array
*/
public function getShares($resourceId) {
$query = $this->db->getQueryBuilder();
$result = $query->select(['principaluri', 'access'])
->from('dav_shares')
->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
->execute();
$shares = [];
while($row = $result->fetch()) {
$shares[]= [
'href' => "principal:${row['principaluri']}",
// 'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '',
'status' => 1,
'readOnly' => ($row['access'] === self::ACCESS_READ),
'{'.\OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD.'}principal' => $row['principaluri']
];
}
return $shares;
}
/**
* For shared resources the sharee is set in the ACL of the resource
*
* @param int $resourceId
* @param array $acl
* @return array
*/
public function applyShareAcl($resourceId, $acl) {
$shares = $this->getShares($resourceId);
foreach ($shares as $share) {
$acl[] = [
'privilege' => '{DAV:}read',
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
'protected' => true,
];
if (!$share['readOnly']) {
$acl[] = [
'privilege' => '{DAV:}write',
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
'protected' => true,
];
}
}
return $acl;
}
}

@ -18,20 +18,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\CardDAV\Sharing;
use Sabre\CardDAV\IAddressBook;
namespace OCA\DAV\DAV\Sharing;
use Sabre\DAV\INode;
/**
* This interface represents a Calendar that can be shared with other users.
* This interface represents a dav resource that can be shared with other users.
*
*/
interface IShareableAddressBook extends IAddressBook {
interface IShareable extends INode {
/**
* Updates the list of shares.
*
* The first array is a list of people that are to be added to the
* addressbook.
* resource.
*
* Every element in the add array has the following properties:
* * href - A url. Usually a mailto: address
@ -48,7 +48,7 @@ interface IShareableAddressBook extends IAddressBook {
function updateShares(array $add, array $remove);
/**
* Returns the list of people whom this addressbook is shared with.
* Returns the list of people whom this resource is shared with.
*
* Every element in this array should have the following properties:
* * href - Often a mailto: address
@ -61,4 +61,14 @@ interface IShareableAddressBook extends IAddressBook {
*/
function getShares();
/**
* @return int
*/
public function getResourceId();
/**
* @return string
*/
public function getOwner();
}

@ -19,7 +19,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\CardDAV\Sharing;
namespace OCA\DAV\DAV\Sharing;
use OCA\DAV\Connector\Sabre\Auth;
use OCP\IRequest;
@ -27,8 +28,6 @@ use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
use Sabre\DAV\XMLUtil;
use Sabre\DAVACL\IACL;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
@ -69,9 +68,7 @@ class Plugin extends ServerPlugin {
* @return string[]
*/
function getFeatures() {
return ['oc-addressbook-sharing'];
return ['oc-resource-sharing'];
}
/**
@ -83,9 +80,7 @@ class Plugin extends ServerPlugin {
* @return string
*/
function getPluginName() {
return 'carddav-sharing';
return 'oc-resource-sharing';
}
/**
@ -101,14 +96,13 @@ class Plugin extends ServerPlugin {
*/
function initialize(Server $server) {
$this->server = $server;
$server->resourceTypeMapping['OCA\\DAV\CardDAV\\ISharedAddressbook'] = '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}shared';
$this->server->xml->elementMap['{' . Plugin::NS_OWNCLOUD . '}share'] = 'OCA\\DAV\\CardDAV\\Sharing\\Xml\\ShareRequest';
$this->server->xml->elementMap['{' . Plugin::NS_OWNCLOUD . '}share'] = 'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest';
$this->server->on('method:POST', [$this, 'httpPost']);
}
/**
* We intercept this to handle POST requests on calendars.
* We intercept this to handle POST requests on a dav resource.
*
* @param RequestInterface $request
* @param ResponseInterface $response
@ -153,11 +147,11 @@ class Plugin extends ServerPlugin {
case '{' . self::NS_OWNCLOUD . '}share' :
// We can only deal with IShareableCalendar objects
if (!$node instanceof IShareableAddressBook) {
if (!$node instanceof IShareable) {
return;
}
$this->server->transactionType = 'post-oc-addressbook-share';
$this->server->transactionType = 'post-oc-resource-share';
// Getting ACL info
$acl = $this->server->getPlugin('acl');

@ -18,9 +18,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\DAV\CardDAV\Sharing\Xml;
namespace OCA\DAV\DAV\Sharing\Xml;
use OCA\DAV\CardDAV\Sharing\Plugin;
use OCA\DAV\DAV\Sharing\Plugin;
use Sabre\Xml\Reader;
use Sabre\Xml\XmlDeserializable;

@ -79,10 +79,9 @@ class Server {
$this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
$this->server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin());
$this->server->addPlugin(new IMipPlugin($mailer, $logger));
$this->server->addPlugin(new \Sabre\CalDAV\SharingPlugin());
$this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin());
$this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin());
$this->server->addPlugin(new CardDAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
$this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
// addressbook plugins
$this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin());

@ -0,0 +1,573 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE caldavtest SYSTEM "caldavtest.dtd">
<!--
Copyright (c) 2006-2015 Apple Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<caldavtest>
<description>Test calendar sharing calendars</description>
<require-feature>
<feature>caldav</feature>
<feature>shared-calendars</feature>
</require-feature>
<start>
<request user="$userid1:" pswd="$pswd1:">
<method>DELETEALL</method>
<ruri>$notificationpath1:/</ruri>
</request>
<request user="$userid2:" pswd="$pswd2:">
<method>DELETEALL</method>
<ruri>$notificationpath2:/</ruri>
</request>
<request end-delete="yes">
<method>MKCALENDAR</method>
<ruri>$calendarhome1:/shared/</ruri>
<verify>
<callback>statusCode</callback>
</verify>
</request>
<request>
<method>PROPPATCH</method>
<ruri>$calendarhome1:/shared/</ruri>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/Common/PROPPATCH/calendar-transp-opaque.xml</filepath>
</data>
</request>
</start>
<test-suite name='Read-write calendar'>
<test name='1'>
<description>POST invitation</description>
<request>
<method>POST</method>
<ruri>$calendarhome1:/shared/</ruri>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/1.xml</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
<test name='2'>
<description>Check Sharee notification collection</description>
<request user="$userid2:" pswd="$pswd2:">
<method>WAITCOUNT 1</method>
<ruri>$notificationpath2:/</ruri>
</request>
<request user="$userid2:" pswd="$pswd2:">
<method>GETNEW</method>
<ruri>$notificationpath2:/</ruri>
<verify>
<callback>xmlDataMatch</callback>
<arg>
<name>filepath</name>
<value>Resource/CalDAV/sharing/calendars/read-write/2.xml</value>
</arg>
<arg>
<name>filter</name>
<value>{http://calendarserver.org/ns/}dtstamp</value>
<value>{http://calendarserver.org/ns/}uid</value>
</arg>
</verify>
<grabelement>
<name>{http://calendarserver.org/ns/}invite-notification/{http://calendarserver.org/ns/}uid</name>
<variable>$inviteuid:</variable>
</grabelement>
</request>
</test>
<test name='3'>
<description>Sharee replies ACCEPTED</description>
<request user="$userid2:" pswd="$pswd2:">
<method>POST</method>
<ruri>$calendarhome2:/</ruri>
<data>
<content-type>application/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/3.xml</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
<grabelement>
<name>{DAV:}href</name>
<variable>$sharedcalendar:</variable>
</grabelement>
</request>
</test>
<test name='4'>
<description>Shared calendar exists</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PROPFIND</method>
<ruri>$sharedcalendar:/</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/4.xml</filepath>
</data>
<verify>
<callback>xmlElementMatch</callback>
<arg>
<name>exists</name>
<value>$verify-property-prefix:/{DAV:}owner/{DAV:}href[=$principaluri1:]</value>
<value>$verify-property-prefix:/{DAV:}resourcetype/{DAV:}collection</value>
<value>$verify-property-prefix:/{DAV:}resourcetype/{urn:ietf:params:xml:ns:caldav}calendar</value>
<value>$verify-property-prefix:/{DAV:}resourcetype/{http://calendarserver.org/ns/}shared</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}read</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}write</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}bind</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}unbind</value>
<value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp/{urn:ietf:params:xml:ns:caldav}transparent</value>
</arg>
<arg>
<name>notexists</name>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}admin</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}all</value>
</arg>
</verify>
</request>
</test>
<test name='4a'>
<description>Shared calendar exists Depth:1</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PROPFIND</method>
<ruri>$calendarhome2:/</ruri>
<header>
<name>Depth</name>
<value>1</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/4.xml</filepath>
</data>
<verify>
<callback>xmlElementMatch</callback>
<arg>
<name>parent</name>
<value>$multistatus-response-prefix:[^{DAV:}href=$sharedcalendar:/]</value>
</arg>
<arg>
<name>exists</name>
<value>$verify-response-prefix:/{DAV:}owner/{DAV:}href[=$principaluri1:]</value>
<value>$verify-response-prefix:/{DAV:}resourcetype/{DAV:}collection</value>
<value>$verify-response-prefix:/{DAV:}resourcetype/{urn:ietf:params:xml:ns:caldav}calendar</value>
<value>$verify-response-prefix:/{DAV:}resourcetype/{http://calendarserver.org/ns/}shared</value>
<value>$verify-response-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}read</value>
<value>$verify-response-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}write</value>
<value>$verify-response-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}bind</value>
<value>$verify-response-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}unbind</value>
<value>$verify-response-prefix:/{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp/{urn:ietf:params:xml:ns:caldav}transparent</value>
</arg>
<arg>
<name>notexists</name>
<value>$verify-response-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}admin</value>
<value>$verify-response-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}all</value>
</arg>
</verify>
</request>
</test>
<test name='4b'>
<description>Shared calendar has invite property</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PROPFIND</method>
<ruri>$sharedcalendar:/</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/5.xml</filepath>
</data>
<verify>
<callback>propfindItems</callback>
<arg>
<name>okprops</name>
<value>{http://calendarserver.org/ns/}invite</value>
</arg>
</verify>
<verify>
<callback>xmlElementMatch</callback>
<arg>
<name>exists</name>
<value>$verify-property-prefix:/{http://calendarserver.org/ns/}invite</value>
<value>$verify-property-prefix:/{http://calendarserver.org/ns/}invite/{http://calendarserver.org/ns/}organizer</value>
<value>$verify-property-prefix:/{http://calendarserver.org/ns/}invite/{http://calendarserver.org/ns/}organizer/{DAV:}href[=$principaluri1:]</value>
<value>$verify-property-prefix:/{http://calendarserver.org/ns/}invite/{http://calendarserver.org/ns/}organizer/{http://calendarserver.org/ns/}common-name[=$username1:]</value>
<value>$verify-property-prefix:/{http://calendarserver.org/ns/}invite/{http://calendarserver.org/ns/}user</value>
<value>$verify-property-prefix:/{http://calendarserver.org/ns/}invite/{http://calendarserver.org/ns/}user/{DAV:}href[=$cuaddrurn2:]</value>
<value>$verify-property-prefix:/{http://calendarserver.org/ns/}invite/{http://calendarserver.org/ns/}user/{http://calendarserver.org/ns/}access/{http://calendarserver.org/ns/}read-write</value>
<value>$verify-property-prefix:/{http://calendarserver.org/ns/}invite/{http://calendarserver.org/ns/}user/{http://calendarserver.org/ns/}invite-accepted</value>
</arg>
</verify>
</request>
</test>
<test name='5'>
<description>Original calendar unchanged</description>
<request>
<method>PROPFIND</method>
<ruri>$calendarhome1:/shared/</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/4.xml</filepath>
</data>
<verify>
<callback>xmlElementMatch</callback>
<arg>
<name>exists</name>
<value>$verify-property-prefix:/{DAV:}owner/{DAV:}href[=$principaluri1:]</value>
<value>$verify-property-prefix:/{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp/{urn:ietf:params:xml:ns:caldav}opaque</value>
</arg>
</verify>
</request>
</test>
<test name='6'>
<description>Sharee creates event</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PUT</method>
<ruri>$sharedcalendar:/1.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/5.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
<test name='7'>
<description>Sharer sees event</description>
<request>
<method>GET</method>
<ruri>$calendarhome1:/shared/1.ics</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
<value>Resource/CalDAV/sharing/calendars/read-write/5.ics</value>
</arg>
</verify>
</request>
</test>
<test name='8'>
<description>Sharer changes event</description>
<request>
<method>PUT</method>
<ruri>$calendarhome1:/shared/1.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/6.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
<test name='9'>
<description>Sharee sees changed event</description>
<request user="$userid2:" pswd="$pswd2:">
<method>GET</method>
<ruri>$sharedcalendar:/1.ics</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
<value>Resource/CalDAV/sharing/calendars/read-write/6.ics</value>
</arg>
</verify>
</request>
</test>
<test name='10'>
<description>Sharer creates event</description>
<request>
<method>PUT</method>
<ruri>$calendarhome1:/shared/2.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/7.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
<test name='11'>
<description>Sharee sees new event</description>
<request user="$userid2:" pswd="$pswd2:">
<method>GET</method>
<ruri>$sharedcalendar:/2.ics</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
<value>Resource/CalDAV/sharing/calendars/read-write/7.ics</value>
</arg>
</verify>
</request>
</test>
<test name='12'>
<description>Sharee changes event</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PUT</method>
<ruri>$sharedcalendar:/2.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-write/8.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
<test name='13'>
<description>Sharer sees changed event</description>
<request>
<method>GET</method>
<ruri>$calendarhome1:/shared/2.ics</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
<value>Resource/CalDAV/sharing/calendars/read-write/8.ics</value>
</arg>
</verify>
</request>
</test>
</test-suite>
<test-suite name='Default calendar cannot be shared calendar'>
<test name='1'>
<description>Set property on Inbox</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PROPPATCH</method>
<ruri>$inboxpath2:/</ruri>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/defaultcalendar/1.xml</filepath>
</data>
<verify>
<callback>propfindItems</callback>
<arg>
<name>badprops</name>
<value>{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL</value>
</arg>
</verify>
</request>
</test>
<test name='2'>
<description>Verify property on inbox</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PROPFIND</method>
<ruri>$inboxpath2:/</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/defaultcalendar/2.xml</filepath>
</data>
<verify>
<callback>propfindItems</callback>
<arg>
<name>okprops</name>
<value><![CDATA[{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL$<href xmlns="DAV:">$calendarpath2:</href>]]></value>
</arg>
</verify>
</request>
</test>
</test-suite>
<test-suite name='Change to read-only calendar'>
<test name='1'>
<description>POST invitation</description>
<request>
<method>POST</method>
<ruri>$calendarhome1:/shared/</ruri>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-only/1.xml</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
<test name='2'>
<description>Check Sharee notification collection</description>
<request user="$userid2:" pswd="$pswd2:">
<method>WAITCOUNT 1</method>
<ruri>$notificationpath2:/</ruri>
</request>
<request user="$userid2:" pswd="$pswd2:">
<method>GETNEW</method>
<ruri>$notificationpath2:/</ruri>
<verify>
<callback>xmlDataMatch</callback>
<arg>
<name>filepath</name>
<value>Resource/CalDAV/sharing/calendars/read-only/2.xml</value>
</arg>
<arg>
<name>filter</name>
<value>{http://calendarserver.org/ns/}dtstamp</value>
<value>{http://calendarserver.org/ns/}uid</value>
</arg>
</verify>
<grabelement>
<name>{http://calendarserver.org/ns/}invite-notification/{http://calendarserver.org/ns/}uid</name>
<variable>$inviteuid:</variable>
</grabelement>
</request>
</test>
<test name='3'>
<description>Sharee replies ACCEPTED</description>
<request user="$userid2:" pswd="$pswd2:">
<method>POST</method>
<ruri>$calendarhome2:/</ruri>
<data>
<content-type>application/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-only/3.xml</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
<grabelement>
<name>{DAV:}href</name>
<variable>$sharedcalendar:</variable>
</grabelement>
</request>
</test>
<test name='4'>
<description>Shared calendar exists</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PROPFIND</method>
<ruri>$sharedcalendar:/</ruri>
<header>
<name>Depth</name>
<value>0</value>
</header>
<data>
<content-type>text/xml; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-only/4.xml</filepath>
</data>
<verify>
<callback>xmlElementMatch</callback>
<arg>
<name>exists</name>
<value>$verify-property-prefix:/{DAV:}owner/{DAV:}href[=$principaluri1:]</value>
<value>$verify-property-prefix:/{DAV:}resourcetype/{http://calendarserver.org/ns/}shared</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}read</value>
</arg>
<arg>
<name>notexists</name>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}write</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}bind</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}unbind</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}admin</value>
<value>$verify-property-prefix:/{DAV:}current-user-privilege-set/{DAV:}privilege/{DAV:}all</value>
</arg>
</verify>
</request>
</test>
<test name='5'>
<description>Create event</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PUT</method>
<ruri>$sharedcalendar:/3.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-only/5.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
<arg>
<name>status</name>
<value>403</value>
</arg>
</verify>
</request>
</test>
<test name='6'>
<description>Sharer creates event</description>
<request>
<method>PUT</method>
<ruri>$calendarhome1:/shared/4.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-only/6.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
</verify>
</request>
</test>
<test name='7'>
<description>Sharee sees new event</description>
<request user="$userid2:" pswd="$pswd2:">
<method>GET</method>
<ruri>$sharedcalendar:/4.ics</ruri>
<verify>
<callback>calendarDataMatch</callback>
<arg>
<name>filepath</name>
<value>Resource/CalDAV/sharing/calendars/read-only/6.ics</value>
</arg>
</verify>
</request>
</test>
<test name='8'>
<description>Sharee cannot change event</description>
<request user="$userid2:" pswd="$pswd2:">
<method>PUT</method>
<ruri>$sharedcalendar:/4.ics</ruri>
<data>
<content-type>text/calendar; charset=utf-8</content-type>
<filepath>Resource/CalDAV/sharing/calendars/read-only/7.ics</filepath>
</data>
<verify>
<callback>statusCode</callback>
<arg>
<name>status</name>
<value>403</value>
</arg>
</verify>
</request>
</test>
</test-suite>
<end>
<request user="$useradmin:" pswd="$pswdadmin:">
<method>DELETEALL</method>
<ruri>$notificationpath1:/</ruri>
<ruri>$notificationpath2:/</ruri>
<ruri>$notificationpath3:/</ruri>
<ruri>$notificationpath4:/</ruri>
</request>
</end>
</caldavtest>

@ -292,13 +292,13 @@ class CardDavBackendTest extends TestCase {
$exampleBook = new AddressBook($this->backend, $books[0]);
$this->backend->updateShares($exampleBook, [['href' => 'principal:principals/best-friend']], []);
$shares = $this->backend->getShares($exampleBook->getBookId());
$shares = $this->backend->getShares($exampleBook->getResourceId());
$this->assertEquals(1, count($shares));
// adding the same sharee again has no effect
$this->backend->updateShares($exampleBook, [['href' => 'principal:principals/best-friend']], []);
$shares = $this->backend->getShares($exampleBook->getBookId());
$shares = $this->backend->getShares($exampleBook->getResourceId());
$this->assertEquals(1, count($shares));
$books = $this->backend->getAddressBooksForUser('principals/best-friend');
@ -306,7 +306,7 @@ class CardDavBackendTest extends TestCase {
$this->backend->updateShares($exampleBook, [], ['principal:principals/best-friend']);
$shares = $this->backend->getShares($exampleBook->getBookId());
$shares = $this->backend->getShares($exampleBook->getResourceId());
$this->assertEquals(0, count($shares));
$books = $this->backend->getAddressBooksForUser('principals/best-friend');
@ -432,6 +432,7 @@ class CardDavBackendTest extends TestCase {
* @dataProvider dataTestSearch
*
* @param string $pattern
* @param array $properties
* @param array $expected
*/
public function testSearch($pattern, $properties, $expected) {

@ -22,8 +22,8 @@
namespace OCA\DAV\Tests\Unit\CardDAV;
use OCA\DAV\CardDAV\Sharing\IShareableAddressBook;
use OCA\DAV\CardDAV\Sharing\Plugin;
use OCA\DAV\DAV\Sharing\IShareable;
use OCA\DAV\DAV\Sharing\Plugin;
use OCA\DAV\Connector\Sabre\Auth;
use OCP\IRequest;
use Sabre\DAV\Server;
@ -38,7 +38,7 @@ class PluginTest extends TestCase {
private $plugin;
/** @var Server */
private $server;
/** @var IShareableAddressBook | \PHPUnit_Framework_MockObject_MockObject */
/** @var IShareable | \PHPUnit_Framework_MockObject_MockObject */
private $book;
public function setUp() {
@ -55,7 +55,7 @@ class PluginTest extends TestCase {
$root = new SimpleCollection('root');
$this->server = new \Sabre\DAV\Server($root);
/** @var SimpleCollection $node */
$this->book = $this->getMockBuilder('OCA\DAV\CardDAV\Sharing\IShareableAddressBook')->disableOriginalConstructor()->getMock();
$this->book = $this->getMockBuilder('OCA\DAV\DAV\Sharing\IShareable')->disableOriginalConstructor()->getMock();
$this->book->method('getName')->willReturn('addressbook1.vcf');
$root->addChild($this->book);
$this->plugin->initialize($this->server);

@ -0,0 +1,83 @@
<?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\Tests\Unit\DAV;
use OCA\DAV\DAV\Sharing\IShareable;
use OCA\DAV\DAV\Sharing\Plugin;
use OCA\DAV\Connector\Sabre\Auth;
use OCP\IRequest;
use Sabre\DAV\Server;
use Sabre\DAV\SimpleCollection;
use Sabre\HTTP\Request;
use Sabre\HTTP\Response;
use Test\TestCase;
class PluginTest extends TestCase {
/** @var Plugin */
private $plugin;
/** @var Server */
private $server;
/** @var IShareable | \PHPUnit_Framework_MockObject_MockObject */
private $book;
public function setUp() {
parent::setUp();
/** @var Auth | \PHPUnit_Framework_MockObject_MockObject $authBackend */
$authBackend = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Auth')->disableOriginalConstructor()->getMock();
$authBackend->method('isDavAuthenticated')->willReturn(true);
/** @var IRequest $request */
$request = $this->getMockBuilder('OCP\IRequest')->disableOriginalConstructor()->getMock();
$this->plugin = new Plugin($authBackend, $request);
$root = new SimpleCollection('root');
$this->server = new \Sabre\DAV\Server($root);
/** @var SimpleCollection $node */
$this->book = $this->getMockBuilder('OCA\DAV\DAV\Sharing\IShareable')->
disableOriginalConstructor()->
getMock();
$this->book->method('getName')->willReturn('addressbook1.vcf');
$root->addChild($this->book);
$this->plugin->initialize($this->server);
}
public function testSharing() {
$this->book->expects($this->once())->method('updateShares')->with([[
'href' => 'principal:principals/admin',
'commonName' => null,
'summary' => null,
'readOnly' => false
]], ['mailto:wilfredo@example.com']);
// setup request
$request = new Request();
$request->addHeader('Content-Type', 'application/xml');
$request->setUrl('addressbook1.vcf');
$request->setBody('<?xml version="1.0" encoding="utf-8" ?><CS:share xmlns:D="DAV:" xmlns:CS="http://owncloud.org/ns"><CS:set><D:href>principal:principals/admin</D:href><CS:read-write/></CS:set> <CS:remove><D:href>mailto:wilfredo@example.com</D:href></CS:remove></CS:share>');
$response = new Response();
$this->plugin->httpPost($request, $response);
}
}

@ -0,0 +1,24 @@
<?php
namespace OCA\DAV\Tests\Unit;
use OCA\DAV\Server;
use OCP\IRequest;
/**
* Class ServerTest
*
* @group DB
*
* @package OCA\DAV\Tests\Unit
*/
class ServerTest extends \Test\TestCase {
public function test() {
/** @var IRequest $r */
$r = $this->getMockBuilder('\OCP\IRequest')
->disableOriginalConstructor()->getMock();
$s = new Server($r, '/');
$this->assertNotNull($s->server);
}
}