Skip to content

Commit 1c1384e

Browse files
miaulalalaChristophWurst
authored andcommitted
feat(dav): expose system address book
Signed-off-by: Anna Larch <[email protected]>
1 parent 9d2d3d4 commit 1c1384e

File tree

9 files changed

+170
-27
lines changed

9 files changed

+170
-27
lines changed

apps/dav/appinfo/v1/carddav.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* @author Morris Jobke <[email protected]>
1111
* @author Thomas Citharel <[email protected]>
1212
* @author Thomas Müller <[email protected]>
13+
* @author Anna Larch <[email protected]>
1314
*
1415
* @license AGPL-3.0
1516
*
@@ -72,7 +73,7 @@
7273
$principalCollection->disableListing = !$debugging; // Disable listing
7374

7475
$pluginManager = new PluginManager(\OC::$server, \OC::$server->query(IAppManager::class));
75-
$addressBookRoot = new AddressBookRoot($principalBackend, $cardDavBackend, $pluginManager);
76+
$addressBookRoot = new AddressBookRoot($principalBackend, $cardDavBackend, $pluginManager, \OC::$server->getUserSession()->getUser(), \OC::$server->get(\OCP\IGroupManager::class));
7677
$addressBookRoot->disableListing = !$debugging; // Disable listing
7778

7879
$nodes = [

apps/dav/lib/CardDAV/AddressBookRoot.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* @author Christoph Wurst <[email protected]>
66
* @author Joas Schilling <[email protected]>
77
* @author Thomas Müller <[email protected]>
8+
* @author Anna Larch <[email protected]>
89
*
910
* @license AGPL-3.0
1011
*
@@ -24,11 +25,15 @@
2425
namespace OCA\DAV\CardDAV;
2526

2627
use OCA\DAV\AppInfo\PluginManager;
28+
use OCP\IGroupManager;
29+
use OCP\IUser;
2730

2831
class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot {
2932

3033
/** @var PluginManager */
3134
private $pluginManager;
35+
private ?IUser $user;
36+
private ?IGroupManager $groupManager;
3237

3338
/**
3439
* @param \Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend
@@ -38,9 +43,13 @@ class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot {
3843
public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $principalBackend,
3944
\Sabre\CardDAV\Backend\BackendInterface $carddavBackend,
4045
PluginManager $pluginManager,
41-
$principalPrefix = 'principals') {
46+
?IUser $user,
47+
?IGroupManager $groupManager,
48+
string $principalPrefix = 'principals') {
4249
parent::__construct($principalBackend, $carddavBackend, $principalPrefix);
4350
$this->pluginManager = $pluginManager;
51+
$this->user = $user;
52+
$this->groupManager = $groupManager;
4453
}
4554

4655
/**
@@ -55,7 +64,7 @@ public function __construct(\Sabre\DAVACL\PrincipalBackend\BackendInterface $pri
5564
* @return \Sabre\DAV\INode
5665
*/
5766
public function getChildForPrincipal(array $principal) {
58-
return new UserAddressBooks($this->carddavBackend, $principal['uri'], $this->pluginManager);
67+
return new UserAddressBooks($this->carddavBackend, $principal['uri'], $this->pluginManager, $this->user, $this->groupManager);
5968
}
6069

6170
public function getName() {

apps/dav/lib/CardDAV/CardDavBackend.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,11 @@ public function getAddressBooksByUri(string $principal, string $addressBookUri):
311311
'{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0',
312312
];
313313

314+
// system address books are always read only
315+
if ($principal === 'principals/system/system') {
316+
$addressBook['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only'] = true;
317+
}
318+
314319
$this->addOwnerPrincipal($addressBook);
315320

316321
return $addressBook;

apps/dav/lib/CardDAV/SyncService.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* @author Morris Jobke <[email protected]>
1111
* @author Thomas Citharel <[email protected]>
1212
* @author Thomas Müller <[email protected]>
13+
* @author Anna Larch <[email protected]>
1314
*
1415
* @license AGPL-3.0
1516
*
@@ -209,10 +210,8 @@ private function parseMultiStatus($body) {
209210
public function updateUser(IUser $user) {
210211
$systemAddressBook = $this->getLocalSystemAddressBook();
211212
$addressBookId = $systemAddressBook['id'];
212-
$name = $user->getBackendClassName();
213-
$userId = $user->getUID();
214213

215-
$cardId = "$name:$userId.vcf";
214+
$cardId = self::getCardUri($user);
216215
if ($user->isEnabled()) {
217216
$card = $this->backend->getCard($addressBookId, $cardId);
218217
if ($card === false) {
@@ -239,10 +238,7 @@ public function updateUser(IUser $user) {
239238
public function deleteUser($userOrCardId) {
240239
$systemAddressBook = $this->getLocalSystemAddressBook();
241240
if ($userOrCardId instanceof IUser) {
242-
$name = $userOrCardId->getBackendClassName();
243-
$userId = $userOrCardId->getUID();
244-
245-
$userOrCardId = "$name:$userId.vcf";
241+
$userOrCardId = self::getCardUri($userOrCardId);
246242
}
247243
$this->backend->deleteCard($systemAddressBook['id'], $userOrCardId);
248244
}
@@ -281,4 +277,12 @@ public function syncInstance(\Closure $progressCallback = null) {
281277
}
282278
}
283279
}
280+
281+
/**
282+
* @param IUser $user
283+
* @return string
284+
*/
285+
public static function getCardUri(IUser $user): string {
286+
return $user->getBackendClassName() . ':' . $user->getUID() . '.vcf';
287+
}
284288
}

apps/dav/lib/CardDAV/SystemAddressbook.php

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @author Joas Schilling <[email protected]>
99
* @author Julius Härtl <[email protected]>
1010
* @author Roeland Jago Douma <[email protected]>
11+
* @author Anna Larch <[email protected]>
1112
*
1213
* @license GNU AGPL version 3 or any later version
1314
*
@@ -31,38 +32,120 @@
3132
use OCA\Federation\TrustedServers;
3233
use OCP\Accounts\IAccountManager;
3334
use OCP\IConfig;
35+
use OCP\IGroupManager;
3436
use OCP\IL10N;
3537
use OCP\IRequest;
38+
use OCP\IUser;
39+
use OCP\IUserSession;
3640
use Sabre\CardDAV\Backend\SyncSupport;
3741
use Sabre\CardDAV\Backend\BackendInterface;
3842
use Sabre\CardDAV\Card;
3943
use Sabre\DAV\Exception\Forbidden;
4044
use Sabre\DAV\Exception\NotFound;
45+
use Sabre\DAV\ICollection;
4146
use Sabre\VObject\Component\VCard;
4247
use Sabre\VObject\Reader;
48+
use function array_unique;
4349

4450
class SystemAddressbook extends AddressBook {
51+
public const URI_SHARED = 'z-server-generated--system';
4552
/** @var IConfig */
4653
private $config;
54+
private IUserSession $userSession;
4755
private ?TrustedServers $trustedServers;
4856
private ?IRequest $request;
57+
private ?IGroupManager $groupManager;
4958

50-
public function __construct(BackendInterface $carddavBackend, array $addressBookInfo, IL10N $l10n, IConfig $config, ?IRequest $request = null, ?TrustedServers $trustedServers = null) {
59+
public function __construct(BackendInterface $carddavBackend,
60+
array $addressBookInfo,
61+
IL10N $l10n,
62+
IConfig $config,
63+
IUserSession $userSession,
64+
?IRequest $request = null,
65+
?TrustedServers $trustedServers = null,
66+
?IGroupManager $groupManager) {
5167
parent::__construct($carddavBackend, $addressBookInfo, $l10n);
5268
$this->config = $config;
69+
$this->userSession = $userSession;
5370
$this->request = $request;
5471
$this->trustedServers = $trustedServers;
72+
$this->groupManager = $groupManager;
73+
74+
$this->addressBookInfo['{DAV:}displayname'] = $l10n->t('Accounts');
75+
$this->addressBookInfo['{' . Plugin::NS_CARDDAV . '}addressbook-description'] = $l10n->t('System address book which holds all accounts');
5576
}
5677

57-
public function getChildren(): array {
78+
/**
79+
* Returns a list of properties for this nodes.
80+
*
81+
* The properties list is a list of propertynames the client requested,
82+
* encoded in clark-notation {xmlnamespace}tagname
83+
*
84+
* If the array is empty, it means 'all properties' were requested.
85+
*
86+
* @param array $properties
87+
*
88+
* @return array
89+
*/
90+
public function getProperties($properties) {
91+
$response = [];
92+
foreach ($properties as $propertyName) {
93+
if (isset($this->addressBookInfo[$propertyName])) {
94+
$response[$propertyName] = $this->addressBookInfo[$propertyName];
95+
}
96+
}
97+
98+
return $response;
99+
}
100+
101+
/**
102+
* No checkbox checked -> Show only the same user
103+
* 'Allow username autocompletion in share dialog' -> show everyone
104+
* 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users within the same groups' -> show only users in intersecting groups
105+
* 'Allow username autocompletion in share dialog' + 'Allow username autocompletion to users based on phone number integration' -> show only the same user
106+
* '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
107+
*/
108+
public function getChildren() {
58109
$shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
59110
$shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
60111
$shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
61-
if (!$shareEnumeration || $shareEnumerationGroup || $shareEnumerationPhone) {
112+
$user = $this->userSession->getUser();
113+
if (!$user) {
114+
// Should never happen because we don't allow anonymous access
62115
return [];
63116
}
117+
if (!$shareEnumeration || !$shareEnumerationGroup && $shareEnumerationPhone) {
118+
$name = SyncService::getCardUri($user);
119+
try {
120+
return [parent::getChild($name)];
121+
} catch (NotFound $e) {
122+
return [];
123+
}
124+
}
125+
if ($shareEnumerationGroup) {
126+
if ($this->groupManager === null) {
127+
// Group manager is not available, so we can't determine which data is safe
128+
return [];
129+
}
130+
$groups = $this->groupManager->getUserGroups($user);
131+
$names = [];
132+
foreach ($groups as $group) {
133+
$users = $group->getUsers();
134+
foreach ($users as $groupUser) {
135+
if ($groupUser->getBackendClassName() === 'Guests') {
136+
continue;
137+
}
138+
$names[] = SyncService::getCardUri($groupUser);
139+
}
140+
}
141+
return parent::getMultipleChildren(array_unique($names));
142+
}
64143

65-
return parent::getChildren();
144+
$children = parent::getChildren();
145+
return array_filter($children, function (Card $child) {
146+
// check only for URIs that begin with Guests:
147+
return strpos($child->getName(), 'Guests:') !== 0;
148+
});
66149
}
67150

68151
/**

apps/dav/lib/CardDAV/UserAddressBooks.php

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* @author Joas Schilling <[email protected]>
1010
* @author Roeland Jago Douma <[email protected]>
1111
* @author Thomas Müller <[email protected]>
12+
* @author Anna Larch <[email protected]>
1213
*
1314
* @license AGPL-3.0
1415
*
@@ -33,8 +34,11 @@
3334
use OCA\Federation\TrustedServers;
3435
use OCP\AppFramework\QueryException;
3536
use OCP\IConfig;
37+
use OCP\IGroupManager;
3638
use OCP\IL10N;
3739
use OCP\IRequest;
40+
use OCP\IUser;
41+
use OCP\IUserSession;
3842
use Psr\Container\ContainerExceptionInterface;
3943
use Psr\Container\NotFoundExceptionInterface;
4044
use Sabre\CardDAV\Backend;
@@ -44,7 +48,6 @@
4448
use Sabre\DAV\MkCol;
4549

4650
class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
47-
4851
/** @var IL10N */
4952
protected $l10n;
5053

@@ -53,12 +56,18 @@ class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
5356

5457
/** @var PluginManager */
5558
private $pluginManager;
59+
private ?IUser $user;
60+
private ?IGroupManager $groupManager;
5661

5762
public function __construct(Backend\BackendInterface $carddavBackend,
5863
string $principalUri,
59-
PluginManager $pluginManager) {
64+
PluginManager $pluginManager,
65+
?IUser $user,
66+
?IGroupManager $groupManager) {
6067
parent::__construct($carddavBackend, $principalUri);
6168
$this->pluginManager = $pluginManager;
69+
$this->user = $user;
70+
$this->groupManager = $groupManager;
6271
}
6372

6473
/**
@@ -74,10 +83,25 @@ public function getChildren() {
7483
$this->config = \OC::$server->getConfig();
7584
}
7685

86+
/** @var string|array $principal */
87+
$principal = $this->principalUri;
7788
$addressBooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
78-
/** @var IAddressBook[] $objects */
79-
$objects = array_map(function (array $addressBook) {
80-
if ($addressBook['principaluri'] === 'principals/system/system') {
89+
// add the system address book
90+
$systemAddressBook = null;
91+
if (is_string($principal) && $principal !== 'principals/system/system' && $this->carddavBackend instanceof CardDavBackend) {
92+
$systemAddressBook = $this->carddavBackend->getAddressBooksByUri('principals/system/system', 'system');
93+
if ($systemAddressBook !== null) {
94+
$systemAddressBook['uri'] = SystemAddressbook::URI_SHARED;
95+
}
96+
}
97+
if (!is_null($systemAddressBook)) {
98+
$addressBooks[] = $systemAddressBook;
99+
}
100+
101+
$objects = [];
102+
if (!empty($addressBooks)) {
103+
/** @var IAddressBook[] $objects */
104+
$objects = array_map(function (array $addressBook) {
81105
$trustedServers = null;
82106
$request = null;
83107
try {
@@ -86,11 +110,22 @@ public function getChildren() {
86110
} catch (NotFoundExceptionInterface | ContainerExceptionInterface $e) {
87111
// nothing to do, the request / trusted servers don't exist
88112
}
89-
return new SystemAddressbook($this->carddavBackend, $addressBook, $this->l10n, $this->config, $request, $trustedServers);
90-
}
113+
if ($addressBook['principaluri'] === 'principals/system/system') {
114+
return new SystemAddressbook(
115+
$this->carddavBackend,
116+
$addressBook,
117+
$this->l10n,
118+
$this->config,
119+
\OCP\Server::get(IUserSession::class),
120+
$request,
121+
$trustedServers,
122+
$this->groupManager
123+
);
124+
}
91125

92-
return new AddressBook($this->carddavBackend, $addressBook, $this->l10n);
93-
}, $addressBooks);
126+
return new AddressBook($this->carddavBackend, $addressBook, $this->l10n);
127+
}, $addressBooks);
128+
}
94129
/** @var IAddressBook[][] $objectsFromPlugins */
95130
$objectsFromPlugins = array_map(function (IAddressBookProvider $plugin): array {
96131
return $plugin->fetchAllForAddressBookHome($this->principalUri);

apps/dav/lib/RootCollection.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
use OCP\EventDispatcher\IEventDispatcher;
5151
use OCP\Files\IRootFolder;
5252
use OCP\IConfig;
53+
use OCP\IGroupManager;
5354
use Psr\Log\LoggerInterface;
5455
use Sabre\DAV\SimpleCollection;
5556

@@ -144,11 +145,11 @@ public function __construct() {
144145

145146
$pluginManager = new PluginManager(\OC::$server, \OC::$server->query(IAppManager::class));
146147
$usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher);
147-
$usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, 'principals/users');
148+
$usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/users');
148149
$usersAddressBookRoot->disableListing = $disableListing;
149150

150151
$systemCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher);
151-
$systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, 'principals/system');
152+
$systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/system');
152153
$systemAddressBookRoot->disableListing = $disableListing;
153154

154155
$uploadCollection = new Upload\RootCollection(

0 commit comments

Comments
 (0)