aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnna Larch <anna@nextcloud.com>2023-05-11 18:59:30 +0200
committerAnna Larch <anna@nextcloud.com>2023-05-11 18:59:30 +0200
commit5c2eb732f123c907201a2db36f12f0a269722ce7 (patch)
tree93f3ac458e66635c7546cf33c81080e00962d088
parent9d2d3d482bce004d134dd87d7b37c3dfb6513412 (diff)
downloadnextcloud-server-5c2eb732f123c907201a2db36f12f0a269722ce7.tar.gz
nextcloud-server-5c2eb732f123c907201a2db36f12f0a269722ce7.zip
fix(carddav): expose system address book
Signed-off-by: Anna Larch <anna@nextcloud.com>
-rw-r--r--apps/dav/appinfo/v1/carddav.php3
-rw-r--r--apps/dav/lib/CardDAV/AddressBookRoot.php13
-rw-r--r--apps/dav/lib/CardDAV/CardDavBackend.php5
-rw-r--r--apps/dav/lib/CardDAV/SyncService.php18
-rw-r--r--apps/dav/lib/CardDAV/SystemAddressbook.php79
-rw-r--r--apps/dav/lib/CardDAV/UserAddressBooks.php53
-rw-r--r--apps/dav/lib/RootCollection.php5
-rw-r--r--apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php5
-rw-r--r--apps/settings/templates/settings/admin/sharing.php4
9 files changed, 158 insertions, 27 deletions
diff --git a/apps/dav/appinfo/v1/carddav.php b/apps/dav/appinfo/v1/carddav.php
index 2b3001e80bd..e7faa9314e2 100644
--- a/apps/dav/appinfo/v1/carddav.php
+++ b/apps/dav/appinfo/v1/carddav.php
@@ -10,6 +10,7 @@
* @author Morris Jobke <hey@morrisjobke.de>
* @author Thomas Citharel <nextcloud@tcit.fr>
* @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Anna Larch <anna.larch@gmx.net>
*
* @license AGPL-3.0
*
@@ -72,7 +73,7 @@ $principalCollection = new \Sabre\CalDAV\Principal\Collection($principalBackend)
$principalCollection->disableListing = !$debugging; // Disable listing
$pluginManager = new PluginManager(\OC::$server, \OC::$server->query(IAppManager::class));
-$addressBookRoot = new AddressBookRoot($principalBackend, $cardDavBackend, $pluginManager);
+$addressBookRoot = new AddressBookRoot($principalBackend, $cardDavBackend, $pluginManager, \OC::$server->getUserSession()->getUser(), \OC::$server->get(\OCP\IGroupManager::class));
$addressBookRoot->disableListing = !$debugging; // Disable listing
$nodes = [
diff --git a/apps/dav/lib/CardDAV/AddressBookRoot.php b/apps/dav/lib/CardDAV/AddressBookRoot.php
index 897ed819071..c82943d2879 100644
--- a/apps/dav/lib/CardDAV/AddressBookRoot.php
+++ b/apps/dav/lib/CardDAV/AddressBookRoot.php
@@ -5,6 +5,7 @@
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Anna Larch <anna.larch@gmx.net>
*
* @license AGPL-3.0
*
@@ -24,11 +25,15 @@
namespace OCA\DAV\CardDAV;
use OCA\DAV\AppInfo\PluginManager;
+use OCP\IGroupManager;
+use OCP\IUser;
class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot {
/** @var PluginManager */
private $pluginManager;
+ private ?IUser $user;
+ private ?IGroupManager $groupManager;
/**
* @param \Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend
@@ -38,9 +43,13 @@ class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot {
public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend,
\Sabre\CardDAV\Backend\BackendInterface $carddavBackend,
PluginManager $pluginManager,
- $principalPrefix = 'principals') {
+ ?IUser $user,
+ ?IGroupManager $groupManager,
+ string $principalPrefix = 'principals') {
parent::__construct($principalBackend, $carddavBackend, $principalPrefix);
$this->pluginManager = $pluginManager;
+ $this->user = $user;
+ $this->groupManager = $groupManager;
}
/**
@@ -55,7 +64,7 @@ class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot {
* @return \Sabre\DAV\INode
*/
public function getChildForPrincipal(array $principal) {
- return new UserAddressBooks($this->carddavBackend, $principal['uri'], $this->pluginManager);
+ return new UserAddressBooks($this->carddavBackend, $principal['uri'], $this->pluginManager, $this->user, $this->groupManager);
}
public function getName() {
diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php
index 577d7282eae..5f5b8f1e65f 100644
--- a/apps/dav/lib/CardDAV/CardDavBackend.php
+++ b/apps/dav/lib/CardDAV/CardDavBackend.php
@@ -311,6 +311,11 @@ class CardDavBackend implements BackendInterface, SyncSupport {
'{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0',
];
+ // system address books are always read only
+ if ($principal === 'principals/system/system') {
+ $addressBook['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only'] = true;
+ }
+
$this->addOwnerPrincipal($addressBook);
return $addressBook;
diff --git a/apps/dav/lib/CardDAV/SyncService.php b/apps/dav/lib/CardDAV/SyncService.php
index da798c5768e..b228d45f067 100644
--- a/apps/dav/lib/CardDAV/SyncService.php
+++ b/apps/dav/lib/CardDAV/SyncService.php
@@ -10,6 +10,7 @@
* @author Morris Jobke <hey@morrisjobke.de>
* @author Thomas Citharel <nextcloud@tcit.fr>
* @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Anna Larch <anna.larch@gmx.net>
*
* @license AGPL-3.0
*
@@ -209,10 +210,8 @@ class SyncService {
public function updateUser(IUser $user) {
$systemAddressBook = $this->getLocalSystemAddressBook();
$addressBookId = $systemAddressBook['id'];
- $name = $user->getBackendClassName();
- $userId = $user->getUID();
- $cardId = "$name:$userId.vcf";
+ $cardId = self::getCardUri($user);
if ($user->isEnabled()) {
$card = $this->backend->getCard($addressBookId, $cardId);
if ($card === false) {
@@ -239,10 +238,7 @@ class SyncService {
public function deleteUser($userOrCardId) {
$systemAddressBook = $this->getLocalSystemAddressBook();
if ($userOrCardId instanceof IUser) {
- $name = $userOrCardId->getBackendClassName();
- $userId = $userOrCardId->getUID();
-
- $userOrCardId = "$name:$userId.vcf";
+ $userOrCardId = self::getCardUri($userOrCardId);
}
$this->backend->deleteCard($systemAddressBook['id'], $userOrCardId);
}
@@ -281,4 +277,12 @@ class SyncService {
}
}
}
+
+ /**
+ * @param IUser $user
+ * @return string
+ */
+ public static function getCardUri(IUser $user): string {
+ return $user->getBackendClassName() . ':' . $user->getUID() . '.vcf';
+ }
}
diff --git a/apps/dav/lib/CardDAV/SystemAddressbook.php b/apps/dav/lib/CardDAV/SystemAddressbook.php
index a803a1e6b24..17900fd033e 100644
--- a/apps/dav/lib/CardDAV/SystemAddressbook.php
+++ b/apps/dav/lib/CardDAV/SystemAddressbook.php
@@ -8,6 +8,7 @@ declare(strict_types=1);
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Roeland Jago Douma <roeland@famdouma.nl>
+ * @author Anna Larch <anna.larch@gmx.net>
*
* @license GNU AGPL version 3 or any later version
*
@@ -31,38 +32,97 @@ use OCA\DAV\Exception\UnsupportedLimitOnInitialSyncException;
use OCA\Federation\TrustedServers;
use OCP\Accounts\IAccountManager;
use OCP\IConfig;
+use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IRequest;
+use OCP\IUser;
+use OCP\IUserSession;
use Sabre\CardDAV\Backend\SyncSupport;
use Sabre\CardDAV\Backend\BackendInterface;
use Sabre\CardDAV\Card;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\ICollection;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Reader;
+use function array_unique;
class SystemAddressbook extends AddressBook {
+ public const URI_SHARED = 'z-server-generated--system';
/** @var IConfig */
private $config;
+ private IUserSession $userSession;
private ?TrustedServers $trustedServers;
private ?IRequest $request;
+ private ?IGroupManager $groupManager;
- public function __construct(BackendInterface $carddavBackend, array $addressBookInfo, IL10N $l10n, IConfig $config, ?IRequest $request = null, ?TrustedServers $trustedServers = null) {
+ public function __construct(BackendInterface $carddavBackend,
+ array $addressBookInfo,
+ IL10N $l10n,
+ IConfig $config,
+ IUserSession $userSession,
+ ?IRequest $request = null,
+ ?TrustedServers $trustedServers = null,
+ ?IGroupManager $groupManager) {
parent::__construct($carddavBackend, $addressBookInfo, $l10n);
$this->config = $config;
+ $this->userSession = $userSession;
$this->request = $request;
$this->trustedServers = $trustedServers;
+ $this->groupManager = $groupManager;
+
+ $this->addressBookInfo['{DAV:}displayname'] = $l10n->t('Accounts');
+ $this->addressBookInfo['{' . Plugin::NS_CARDDAV . '}addressbook-description'] = $l10n->t('System address book which holds all accounts');
}
- public function getChildren(): array {
+ /**
+ * No checkbox checked -> Show only the same user
+ * 'Allow username autocompletion in share dialog' -> show everyone
+ * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users within the same groups' -> show only users in intersecting groups
+ * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users based on phone number integration' -> show only the same user
+ * 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users within the same groups' + 'Allow username autocompletion to users based on phone number integration' -> show only users in intersecting groups
+ */
+ public function getChildren() {
$shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
$shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
$shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
- if (!$shareEnumeration || $shareEnumerationGroup || $shareEnumerationPhone) {
+ $user = $this->userSession->getUser();
+ if (!$user) {
+ // Should never happen because we don't allow anonymous access
return [];
}
+ if (!$shareEnumeration || !$shareEnumerationGroup && $shareEnumerationPhone) {
+ $name = SyncService::getCardUri($user);
+ try {
+ return [parent::getChild($name)];
+ } catch (NotFound $e) {
+ return [];
+ }
+ }
+ if ($shareEnumerationGroup) {
+ if ($this->groupManager === null) {
+ // Group manager is not available, so we can't determine which data is safe
+ return [];
+ }
+ $groups = $this->groupManager->getUserGroups($user);
+ $names = [];
+ foreach ($groups as $group) {
+ $users = $group->getUsers();
+ foreach ($users as $groupUser) {
+ if ($groupUser->getBackendClassName() === 'Guests') {
+ continue;
+ }
+ $names[] = SyncService::getCardUri($groupUser);
+ }
+ }
+ return parent::getMultipleChildren(array_unique($names));
+ }
- return parent::getChildren();
+ $children = parent::getChildren();
+ return array_filter($children, function (Card $child) {
+ // check only for URIs that begin with Guests:
+ return strpos($child->getName(), 'Guests:') !== 0;
+ });
}
/**
@@ -225,4 +285,15 @@ class SystemAddressbook extends AddressBook {
return $vCard->serialize();
}
+
+ /**
+ * @return mixed
+ * @throws Forbidden
+ */
+ public function delete() {
+ if ($this->isFederation()) {
+ parent::delete();
+ }
+ throw new Forbidden();
+ }
}
diff --git a/apps/dav/lib/CardDAV/UserAddressBooks.php b/apps/dav/lib/CardDAV/UserAddressBooks.php
index 85795604f28..938575bd1a7 100644
--- a/apps/dav/lib/CardDAV/UserAddressBooks.php
+++ b/apps/dav/lib/CardDAV/UserAddressBooks.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
* @author Joas Schilling <coding@schilljs.com>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Anna Larch <anna.larch@gmx.net>
*
* @license AGPL-3.0
*
@@ -33,8 +34,11 @@ use OCA\DAV\CardDAV\Integration\ExternalAddressBook;
use OCA\Federation\TrustedServers;
use OCP\AppFramework\QueryException;
use OCP\IConfig;
+use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IRequest;
+use OCP\IUser;
+use OCP\IUserSession;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Sabre\CardDAV\Backend;
@@ -44,7 +48,6 @@ use function array_map;
use Sabre\DAV\MkCol;
class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
-
/** @var IL10N */
protected $l10n;
@@ -53,12 +56,18 @@ class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
/** @var PluginManager */
private $pluginManager;
+ private ?IUser $user;
+ private ?IGroupManager $groupManager;
public function __construct(Backend\BackendInterface $carddavBackend,
string $principalUri,
- PluginManager $pluginManager) {
+ PluginManager $pluginManager,
+ ?IUser $user,
+ ?IGroupManager $groupManager) {
parent::__construct($carddavBackend, $principalUri);
$this->pluginManager = $pluginManager;
+ $this->user = $user;
+ $this->groupManager = $groupManager;
}
/**
@@ -74,10 +83,25 @@ class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
$this->config = \OC::$server->getConfig();
}
+ /** @var string|array $principal */
+ $principal = $this->principalUri;
$addressBooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
- /** @var IAddressBook[] $objects */
- $objects = array_map(function (array $addressBook) {
- if ($addressBook['principaluri'] === 'principals/system/system') {
+ // add the system address book
+ $systemAddressBook = null;
+ if (is_string($principal) && $principal !== 'principals/system/system' && $this->carddavBackend instanceof CardDavBackend) {
+ $systemAddressBook = $this->carddavBackend->getAddressBooksByUri('principals/system/system', 'system');
+ if ($systemAddressBook !== null) {
+ $systemAddressBook['uri'] = SystemAddressbook::URI_SHARED;
+ }
+ }
+ if (!is_null($systemAddressBook)) {
+ $addressBooks[] = $systemAddressBook;
+ }
+
+ $objects = [];
+ if (!empty($addressBooks)) {
+ /** @var IAddressBook[] $objects */
+ $objects = array_map(function (array $addressBook) {
$trustedServers = null;
$request = null;
try {
@@ -86,11 +110,22 @@ class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
} catch (NotFoundExceptionInterface | ContainerExceptionInterface $e) {
// nothing to do, the request / trusted servers don't exist
}
- return new SystemAddressbook($this->carddavBackend, $addressBook, $this->l10n, $this->config, $request, $trustedServers);
- }
+ if ($addressBook['principaluri'] === 'principals/system/system') {
+ return new SystemAddressbook(
+ $this->carddavBackend,
+ $addressBook,
+ $this->l10n,
+ $this->config,
+ \OCP\Server::get(IUserSession::class),
+ $request,
+ $trustedServers,
+ $this->groupManager
+ );
+ }
- return new AddressBook($this->carddavBackend, $addressBook, $this->l10n);
- }, $addressBooks);
+ return new AddressBook($this->carddavBackend, $addressBook, $this->l10n);
+ }, $addressBooks);
+ }
/** @var IAddressBook[][] $objectsFromPlugins */
$objectsFromPlugins = array_map(function (IAddressBookProvider $plugin): array {
return $plugin->fetchAllForAddressBookHome($this->principalUri);
diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php
index 6e009875ddc..80d96f0d748 100644
--- a/apps/dav/lib/RootCollection.php
+++ b/apps/dav/lib/RootCollection.php
@@ -50,6 +50,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\IRootFolder;
use OCP\IConfig;
+use OCP\IGroupManager;
use Psr\Log\LoggerInterface;
use Sabre\DAV\SimpleCollection;
@@ -144,11 +145,11 @@ class RootCollection extends SimpleCollection {
$pluginManager = new PluginManager(\OC::$server, \OC::$server->query(IAppManager::class));
$usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher);
- $usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, 'principals/users');
+ $usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/users');
$usersAddressBookRoot->disableListing = $disableListing;
$systemCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher);
- $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, 'principals/system');
+ $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/system');
$systemAddressBookRoot->disableListing = $disableListing;
$uploadCollection = new Upload\RootCollection(
diff --git a/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php b/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php
index 73393d75615..a753a1c5a73 100644
--- a/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php
+++ b/apps/dav/tests/unit/CardDAV/SystemAddressBookTest.php
@@ -32,6 +32,7 @@ use OCP\Accounts\IAccountManager;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
+use OCP\IUserSession;
use PHPUnit\Framework\MockObject\MockObject;
use Sabre\CardDAV\Backend\BackendInterface;
use Sabre\VObject\Component\VCard;
@@ -44,6 +45,7 @@ class SystemAddressBookTest extends TestCase {
private array $addressBookInfo;
private IL10N|MockObject $l10n;
private IConfig|MockObject $config;
+ private IUserSession $userSession;
private IRequest|MockObject $request;
private array $server;
private TrustedServers|MockObject $trustedServers;
@@ -60,6 +62,7 @@ class SystemAddressBookTest extends TestCase {
];
$this->l10n = $this->createMock(IL10N::class);
$this->config = $this->createMock(IConfig::class);
+ $this->userSession = $this->createMock(IUserSession::class);
$this->request = $this->createMock(Request::class);
$this->server = [
'PHP_AUTH_USER' => 'system',
@@ -73,8 +76,10 @@ class SystemAddressBookTest extends TestCase {
$this->addressBookInfo,
$this->l10n,
$this->config,
+ $this->userSession,
$this->request,
$this->trustedServers,
+ null,
);
}
diff --git a/apps/settings/templates/settings/admin/sharing.php b/apps/settings/templates/settings/admin/sharing.php
index 781fdf3a49d..709c36a7b7b 100644
--- a/apps/settings/templates/settings/admin/sharing.php
+++ b/apps/settings/templates/settings/admin/sharing.php
@@ -209,7 +209,7 @@
<?php if ($_['allowShareDialogUserEnumeration'] === 'yes') {
print_unescaped('checked="checked"');
} ?> />
- <label for="shareapi_allow_share_dialog_user_enumeration"><?php p($l->t('Allow username autocompletion in share dialog'));?></label><br />
+ <label for="shareapi_allow_share_dialog_user_enumeration"><?php p($l->t('Allow username autocompletion in share dialog and allow access to the system address book'));?></label><br />
</p>
<p id="shareapi_restrict_user_enumeration_to_group_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {
@@ -219,7 +219,7 @@
<?php if ($_['restrictUserEnumerationToGroup'] === 'yes') {
print_unescaped('checked="checked"');
} ?> />
- <label for="shareapi_restrict_user_enumeration_to_group"><?php p($l->t('Allow username autocompletion to users within the same groups'));?></label><br />
+ <label for="shareapi_restrict_user_enumeration_to_group"><?php p($l->t('Allow username autocompletion to users within the same groups and limit system address books to users in the same groups'));?></label><br />
</p>
<p id="shareapi_restrict_user_enumeration_to_phone_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {