Skip to content

Commit 4b3e9b4

Browse files
committed
fix(autocomplete): respect privacy settings for sharing
Signed-off-by: Hamza <hamzamahjoubi221@gmail.com>
1 parent 7b489f2 commit 4b3e9b4

4 files changed

Lines changed: 281 additions & 91 deletions

File tree

lib/Controller/ContactIntegrationController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function __construct(string $appName,
4949
*/
5050
#[TrapError]
5151
public function match(string $mail): JSONResponse {
52-
return (new JSONResponse($this->service->findMatches($mail)))->cacheFor(60 * 60, false, true);
52+
return (new JSONResponse($this->service->findMatches($this->uid, $mail)))->cacheFor(60 * 60, false, true);
5353
}
5454

5555
/**
@@ -88,7 +88,7 @@ public function newContact(?string $contactName = null, ?string $mail = null): J
8888
*/
8989
#[TrapError]
9090
public function autoComplete(string $term): JSONResponse {
91-
$res = $this->service->autoComplete($term);
91+
$res = $this->service->autoComplete($this->uid, $term);
9292
return (new JSONResponse($res))->cacheFor(60 * 60, false, true);
9393
}
9494
}

lib/Service/ContactIntegration/ContactIntegrationService.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ public function __construct(ContactsIntegration $ci) {
3333
$this->contactsIntegration = $ci;
3434
}
3535

36-
public function findMatches(string $mail): array {
37-
$matches = $this->contactsIntegration->getContactsWithMail($mail);
36+
public function findMatches(string $uid, string $mail): array {
37+
$matches = $this->contactsIntegration->getContactsWithMail($uid, $mail);
3838
return $matches;
3939
}
4040

@@ -46,7 +46,7 @@ public function newContact(string $name, string $mail): ?array {
4646
return $this->contactsIntegration->newContact($name, $mail);
4747
}
4848

49-
public function autoComplete(string $term): array {
50-
return $this->contactsIntegration->getContactsWithName($term);
49+
public function autoComplete(string $uid, string $term): array {
50+
return $this->contactsIntegration->getContactsWithName($uid, $term);
5151
}
5252
}

lib/Service/ContactsIntegration.php

Lines changed: 94 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
use OCP\IConfig;
2929
use OCP\IGroupManager;
3030
use OCP\IUserManager;
31+
use function is_array;
3132

3233
class ContactsIntegration {
3334
/** @var IManager */
@@ -60,55 +61,9 @@ public function __construct(IManager $contactsManager,
6061
* @return array
6162
*/
6263
public function getMatchingRecipient(string $userId, string $term): array {
63-
if (!$this->contactsManager->isEnabled()) {
64-
return [];
65-
}
66-
67-
// If 'Allow username autocompletion in share dialog' is disabled in the admin sharing settings, then we must not
68-
// auto-complete system users
69-
$shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'no') === 'yes';
70-
$shareeEnumerationInGroupOnly = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
71-
$shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
72-
$shareeEnumerationFullMatchUserId = $shareeEnumerationFullMatch && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes') === 'yes';
73-
$shareeEnumerationFullMatchEmail = $shareeEnumerationFullMatch && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
74-
75-
$result = $this->contactsManager->search(
76-
$term,
77-
['UID', 'FN', 'EMAIL'],
78-
[
79-
'enumeration' => $shareeEnumeration,
80-
'fullmatch' => $shareeEnumerationFullMatch,
81-
'limit' => 20,
82-
],
83-
);
84-
if (empty($result)) {
85-
return [];
86-
}
64+
$result = $this->search($userId, $term, ['UID', 'FN', 'EMAIL']);
8765
$receivers = [];
88-
89-
if ($shareeEnumeration && $shareeEnumerationInGroupOnly) {
90-
$user = $this->userManager->get($userId);
91-
if ($user === null) {
92-
return [];
93-
}
94-
$userGroups = $this->groupManager->getUserGroupIds($user);
95-
}
96-
9766
foreach ($result as $r) {
98-
$isSystemUser = isset($r['isLocalSystemBook']) && $r['isLocalSystemBook'];
99-
$isInSameGroup = false;
100-
if ($isSystemUser && $shareeEnumerationInGroupOnly) {
101-
foreach ($userGroups as $userGroup) {
102-
if ($this->groupManager->isInGroup($r['UID'], $userGroup)) {
103-
$isInSameGroup = true;
104-
break;
105-
}
106-
}
107-
if (!$shareeEnumerationFullMatch && !$isInSameGroup) {
108-
continue;
109-
}
110-
}
111-
11267
$id = $r['UID'];
11368
$fn = $r['FN'] ?? null;
11469
if (!isset($r['EMAIL'])) {
@@ -125,23 +80,10 @@ public function getMatchingRecipient(string $userId, string $term): array {
12580
if ($e === '') {
12681
continue;
12782
}
128-
$lowerTerm = strtolower($term);
129-
130-
if ($isSystemUser && $shareeEnumerationInGroupOnly && !$isInSameGroup) {
131-
// Check for full match. If full match is disabled, matching results already filtered out
132-
if (!($lowerTerm !== '' && (
133-
($shareeEnumerationFullMatch && !empty($fn) && $lowerTerm === strtolower($fn)) ||
134-
($shareeEnumerationFullMatchUserId && $lowerTerm === strtolower($id)) ||
135-
($shareeEnumerationFullMatchEmail && $lowerTerm === strtolower($e))))) {
136-
// Not a full Match
137-
continue;
138-
}
139-
}
140-
14183
$receivers[] = [
14284
'id' => $id,
14385
// Show full name if possible or fall back to email
144-
'label' => $fn,
86+
'label' => $fn ?? $e,
14587
'email' => $e,
14688
'photo' => $photo,
14789
'source' => 'contacts',
@@ -243,21 +185,98 @@ public function newContact(string $name, string $mailAddr, string $type = 'HOME'
243185
return $createdContact;
244186
}
245187

188+
private function search(string $userId, string $term, array $fields, ?bool $strictSearch = null): array {
189+
if (!$this->contactsManager->isEnabled()) {
190+
return [];
191+
}
192+
193+
// If 'Allow username autocompletion in share dialog' is disabled in the admin sharing settings, then we must not
194+
// auto-complete system users
195+
$shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
196+
$shareeEnumerationInGroupOnly = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
197+
$shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
198+
$shareeEnumerationFullMatchDisplayName = $shareeEnumerationFullMatch && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_displayname', 'yes') === 'yes';
199+
$shareeEnumerationFullMatchUserId = $shareeEnumerationFullMatch && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes') === 'yes';
200+
$shareeEnumerationFullMatchEmail = $shareeEnumerationFullMatch && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
201+
202+
$options = [
203+
'enumeration' => $shareeEnumeration,
204+
'fullmatch' => $shareeEnumerationFullMatch,
205+
'limit' => 20,
206+
];
207+
if ($strictSearch !== null) {
208+
$options['strict_search'] = $strictSearch;
209+
}
210+
211+
$result = $this->contactsManager->search(
212+
$term,
213+
$fields,
214+
$options,
215+
);
216+
217+
$userGroups = [];
218+
if ($shareeEnumeration && $shareeEnumerationInGroupOnly) {
219+
$user = $this->userManager->get($userId);
220+
if ($user === null) {
221+
return [];
222+
}
223+
$userGroups = $this->groupManager->getUserGroupIds($user);
224+
}
225+
226+
$filteredResults = [];
227+
foreach ($result as $r) {
228+
$isSystemUser = isset($r['isLocalSystemBook']) && $r['isLocalSystemBook'];
229+
$isInSameGroup = false;
230+
if ($isSystemUser && $shareeEnumerationInGroupOnly) {
231+
foreach ($userGroups as $userGroup) {
232+
if ($this->groupManager->isInGroup($r['UID'], $userGroup)) {
233+
$isInSameGroup = true;
234+
break;
235+
}
236+
}
237+
if (!$shareeEnumerationFullMatch && !$isInSameGroup) {
238+
continue;
239+
}
240+
}
241+
242+
if ($isSystemUser && $shareeEnumerationInGroupOnly && !$isInSameGroup && $shareeEnumerationFullMatch) {
243+
// Check for full match. If full match is disabled, non-matching results already filtered out above.
244+
$id = $r['UID'];
245+
$fn = $r['FN'] ?? null;
246+
$lowerTerm = strtolower($term);
247+
$isMatch = ($lowerTerm !== '' && (
248+
($shareeEnumerationFullMatchDisplayName && !empty($fn) && $lowerTerm === strtolower($fn))
249+
|| ($shareeEnumerationFullMatchUserId && $lowerTerm === strtolower($id)))) ;
250+
if ($shareeEnumerationFullMatchEmail && !$isMatch) {
251+
$email = $r['EMAIL'] ?? null;
252+
if ($email === null) {
253+
continue;
254+
}
255+
$emails = is_array($email) ? $email : [$email];
256+
foreach ($emails as $e) {
257+
if ($lowerTerm === strtolower($e)) {
258+
$isMatch = true;
259+
break;
260+
}
261+
}
262+
}
263+
if (!$isMatch) {
264+
continue;
265+
}
266+
}
267+
268+
$filteredResults[] = $r;
269+
}
270+
return $filteredResults;
271+
}
272+
246273
/**
247274
* @param string[] $fields
248275
*/
249-
private function doSearch(string $term, array $fields, bool $strictSearch): array {
250-
$allowSystemUsers = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'no') === 'yes';
251-
252-
$result = $this->contactsManager->search($term, $fields, [
253-
'strict_search' => $strictSearch,
254-
'limit' => 20,
255-
]);
276+
private function doSearch(string $userId, string $term, array $fields, bool $strictSearch) : array {
277+
$result = $this->search($userId, $term, $fields, $strictSearch);
256278
$matches = [];
257279
foreach ($result as $r) {
258-
if (!$allowSystemUsers && isset($r['isLocalSystemBook']) && $r['isLocalSystemBook']) {
259-
continue;
260-
}
261280
$id = $r['UID'];
262281
$fn = $r['FN'];
263282
$matches[] = [
@@ -270,18 +289,15 @@ private function doSearch(string $term, array $fields, bool $strictSearch): arra
270289

271290
/**
272291
* Extracts all Contacts with the specified mail address
273-
*
274-
* @param string $mailAddr
275-
* @return array
276292
*/
277-
public function getContactsWithMail(string $mailAddr) {
278-
return $this->doSearch($mailAddr, ['EMAIL'], true);
293+
public function getContactsWithMail(string $userId, string $mailAddr): array {
294+
return $this->doSearch($userId, $mailAddr, ['EMAIL'], true);
279295
}
280296

281297
/**
282298
* Extracts all Contacts with the specified name
283299
*/
284-
public function getContactsWithName(string $name): array {
285-
return $this->doSearch($name, ['FN'], false);
300+
public function getContactsWithName(string $userId, string $name): array {
301+
return $this->doSearch($userId, $name, ['FN'], false);
286302
}
287303
}

0 commit comments

Comments
 (0)