@@ -68,7 +68,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { | |||
function getShares() { | |||
/** @var CardDavBackend $carddavBackend */ | |||
$carddavBackend = $this->carddavBackend; | |||
return $carddavBackend->getShares($this->getBookId()); | |||
return $carddavBackend->getShares($this->getResourceId()); | |||
} | |||
function getACL() { | |||
@@ -100,7 +100,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { | |||
/** @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 IShareable { | |||
/** @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 IShareable { | |||
/** | |||
* @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'); | |||
} | |||
/** | |||
@@ -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\DAV\Sharing\Plugin::NS_OWNCLOUD.'}principal' => $p['uri'] | |||
]; | |||
} | |||
return $shares; | |||
return $this->sharingBackend->getShares($addressBookId); | |||
} | |||
/** |
@@ -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; | |||
} | |||
} |
@@ -61,4 +61,14 @@ interface IShareable extends INode { | |||
*/ | |||
function getShares(); | |||
/** | |||
* @return int | |||
*/ | |||
public function getResourceId(); | |||
/** | |||
* @return string | |||
*/ | |||
public function getOwner(); | |||
} |
@@ -151,7 +151,7 @@ class Plugin extends ServerPlugin { | |||
return; | |||
} | |||
$this->server->transactionType = 'post-oc-addressbook-share'; | |||
$this->server->transactionType = 'post-oc-resource-share'; | |||
// Getting ACL info | |||
$acl = $this->server->getPlugin('acl'); |
@@ -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) { |