mirror of
https://github.com/nextcloud/server.git
synced 2026-06-08 08:16:43 -04:00
Merge pull request #30248 from nextcloud/backport/30245/stable21
[stable21] Only wildcard search if enumeration is allowed
This commit is contained in:
commit
92744f3047
16 changed files with 114 additions and 22 deletions
|
|
@ -107,6 +107,8 @@ class AddressBookImpl implements IAddressBook {
|
|||
* - 'escape_like_param' - If set to false wildcards _ and % are not escaped
|
||||
* - 'limit' - Set a numeric limit for the search results
|
||||
* - 'offset' - Set the offset for the limited search results
|
||||
* - 'wildcard' - Whether the search should use wildcards
|
||||
* @psalm-param array{types?: bool, escape_like_param?: bool, limit?: int, offset?: int, wildcard?: bool} $options
|
||||
* @return array an array of contacts which are arrays of key-value-pairs
|
||||
* example result:
|
||||
* [
|
||||
|
|
|
|||
|
|
@ -1004,6 +1004,8 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|||
* - 'escape_like_param' - If set to false wildcards _ and % are not escaped, otherwise they are
|
||||
* - 'limit' - Set a numeric limit for the search results
|
||||
* - 'offset' - Set the offset for the limited search results
|
||||
* - 'wildcard' - Whether the search should use wildcards
|
||||
* @psalm-param array{escape_like_param?: bool, limit?: int, offset?: int, wildcard?: bool} $options
|
||||
* @return array an array of contacts which are arrays of key-value-pairs
|
||||
*/
|
||||
public function search($addressBookId, $pattern, $searchProperties, $options = []): array {
|
||||
|
|
@ -1035,6 +1037,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|||
* @param string $pattern
|
||||
* @param array $searchProperties
|
||||
* @param array $options
|
||||
* @psalm-param array{types?: bool, escape_like_param?: bool, limit?: int, offset?: int, wildcard?: bool} $options
|
||||
* @return array
|
||||
*/
|
||||
private function searchByAddressBookIds(array $addressBookIds,
|
||||
|
|
@ -1042,6 +1045,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|||
array $searchProperties,
|
||||
array $options = []): array {
|
||||
$escapePattern = !\array_key_exists('escape_like_param', $options) || $options['escape_like_param'] !== false;
|
||||
$useWildcards = !\array_key_exists('wildcard', $options) || $options['wildcard'] !== false;
|
||||
|
||||
$query2 = $this->db->getQueryBuilder();
|
||||
|
||||
|
|
@ -1083,7 +1087,9 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|||
|
||||
// No need for like when the pattern is empty
|
||||
if ('' !== $pattern) {
|
||||
if (!$escapePattern) {
|
||||
if (!$useWildcards) {
|
||||
$query2->andWhere($query2->expr()->eq('cp.value', $query2->createNamedParameter($pattern)));
|
||||
} elseif (!$escapePattern) {
|
||||
$query2->andWhere($query2->expr()->ilike('cp.value', $query2->createNamedParameter($pattern)));
|
||||
} else {
|
||||
$query2->andWhere($query2->expr()->ilike('cp.value', $query2->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')));
|
||||
|
|
|
|||
|
|
@ -255,7 +255,12 @@ class Notifier implements INotifier {
|
|||
}
|
||||
}
|
||||
|
||||
$addressBookEntries = $this->contactsManager->search($federatedCloudId, ['CLOUD']);
|
||||
$addressBookEntries = $this->contactsManager->search($federatedCloudId, ['CLOUD'], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'fullmatch' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
foreach ($addressBookEntries as $entry) {
|
||||
if (isset($entry['CLOUD'])) {
|
||||
foreach ($entry['CLOUD'] as $cloudID) {
|
||||
|
|
|
|||
|
|
@ -560,7 +560,12 @@ class Provider implements IProvider {
|
|||
return $this->displayNames[$search];
|
||||
}
|
||||
|
||||
$addressBookContacts = $this->contactsManager->search($search, ['CLOUD']);
|
||||
$addressBookContacts = $this->contactsManager->search($search, ['CLOUD'], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'fullmatch' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
foreach ($addressBookContacts as $contact) {
|
||||
if (isset($contact['isLocalSystemBook'])) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -204,7 +204,12 @@ abstract class Base implements IProvider {
|
|||
return $this->displayNames[$search];
|
||||
}
|
||||
|
||||
$addressBookContacts = $this->contactsManager->search($search, ['CLOUD']);
|
||||
$addressBookContacts = $this->contactsManager->search($search, ['CLOUD'], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'fullmatch' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
foreach ($addressBookContacts as $contact) {
|
||||
if (isset($contact['isLocalSystemBook'])) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -333,8 +333,12 @@ class ShareAPIController extends OCSController {
|
|||
* @return string
|
||||
*/
|
||||
private function getDisplayNameFromAddressBook(string $query, string $property): string {
|
||||
// FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered
|
||||
$result = \OC::$server->getContactsManager()->search($query, [$property]);
|
||||
// FIXME: If we inject the contacts manager it gets initialized before any address books are registered
|
||||
$result = \OC::$server->getContactsManager()->search($query, [$property], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
foreach ($result as $r) {
|
||||
foreach ($r[$property] as $value) {
|
||||
if ($value === $query && $r['FN']) {
|
||||
|
|
|
|||
|
|
@ -4227,7 +4227,11 @@ class ShareAPIControllerTest extends TestCase {
|
|||
|
||||
$cm->method('search')
|
||||
->willReturnMap([
|
||||
['user@server.com', ['CLOUD'], [],
|
||||
['user@server.com', ['CLOUD'], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'strict_search' => true,
|
||||
],
|
||||
[
|
||||
[
|
||||
'CLOUD' => [
|
||||
|
|
@ -4237,7 +4241,11 @@ class ShareAPIControllerTest extends TestCase {
|
|||
],
|
||||
],
|
||||
],
|
||||
['user@server.com', ['EMAIL'], [],
|
||||
['user@server.com', ['EMAIL'], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'strict_search' => true,
|
||||
],
|
||||
[
|
||||
[
|
||||
'EMAIL' => [
|
||||
|
|
|
|||
|
|
@ -363,7 +363,12 @@ class Activity implements IProvider {
|
|||
* @return string
|
||||
*/
|
||||
protected function getContactName($email) {
|
||||
$addressBookContacts = $this->contactsManager->search($email, ['EMAIL']);
|
||||
$addressBookContacts = $this->contactsManager->search($email, ['EMAIL'], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'fullmatch' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
|
||||
foreach ($addressBookContacts as $contact) {
|
||||
if (isset($contact['isLocalSystemBook'])) {
|
||||
|
|
|
|||
|
|
@ -87,12 +87,7 @@ class MailPlugin implements ISearchPlugin {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param $search
|
||||
* @param $limit
|
||||
* @param $offset
|
||||
* @param ISearchResult $searchResult
|
||||
* @return bool
|
||||
* @since 13.0.0
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function search($search, $limit, $offset, ISearchResult $searchResult) {
|
||||
$currentUserId = $this->userSession->getUser()->getUID();
|
||||
|
|
@ -102,7 +97,16 @@ class MailPlugin implements ISearchPlugin {
|
|||
$emailType = new SearchResultType('emails');
|
||||
|
||||
// Search in contacts
|
||||
$addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN'], ['limit' => $limit, 'offset' => $offset]);
|
||||
$addressBookContacts = $this->contactsManager->search(
|
||||
$search,
|
||||
['EMAIL', 'FN'],
|
||||
[
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'enumeration' => (bool) $this->shareeEnumeration,
|
||||
'fullmatch' => (bool) $this->shareeEnumerationFullMatch,
|
||||
]
|
||||
);
|
||||
$lowerSearch = strtolower($search);
|
||||
foreach ($addressBookContacts as $contact) {
|
||||
if (isset($contact['EMAIL'])) {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,12 @@ class RemotePlugin implements ISearchPlugin {
|
|||
$resultType = new SearchResultType('remotes');
|
||||
|
||||
// Search in contacts
|
||||
$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN'], ['limit' => $limit, 'offset' => $offset]);
|
||||
$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN'], [
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
'enumeration' => false,
|
||||
'fullmatch' => false,
|
||||
]);
|
||||
foreach ($addressBookContacts as $contact) {
|
||||
if (isset($contact['isLocalSystemBook'])) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -75,7 +75,10 @@ class ContactsStore implements IContactsStore {
|
|||
* @return IEntry[]
|
||||
*/
|
||||
public function getContacts(IUser $user, $filter, ?int $limit = null, ?int $offset = null) {
|
||||
$options = [];
|
||||
$options = [
|
||||
'enumeration' => $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes',
|
||||
'fullmatch' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes',
|
||||
];
|
||||
if ($limit !== null) {
|
||||
$options['limit'] = $limit;
|
||||
}
|
||||
|
|
@ -244,7 +247,9 @@ class ContactsStore implements IContactsStore {
|
|||
}
|
||||
|
||||
$userId = $user->getUID();
|
||||
$allContacts = $this->contactsManager->search($shareWith, $filter);
|
||||
$allContacts = $this->contactsManager->search($shareWith, $filter, [
|
||||
'strict_search' => true,
|
||||
]);
|
||||
$contacts = array_filter($allContacts, function ($contact) use ($userId) {
|
||||
return $contact['UID'] !== $userId;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,13 +43,35 @@ class ContactsManager implements IManager {
|
|||
* - 'escape_like_param' - If set to false wildcards _ and % are not escaped
|
||||
* - 'limit' - Set a numeric limit for the search results
|
||||
* - 'offset' - Set the offset for the limited search results
|
||||
* - 'enumeration' - (since 23.0.0) Whether user enumeration on system address book is allowed
|
||||
* - 'fullmatch' - (since 23.0.0) Whether matching on full detail in system address book is allowed
|
||||
* - 'strict_search' - (since 23.0.0) Whether the search pattern is full string or partial search
|
||||
* @psalm-param array{escape_like_param?: bool, limit?: int, offset?: int, enumeration?: bool, fullmatch?: bool, strict_search?: bool} $options
|
||||
* @return array an array of contacts which are arrays of key-value-pairs
|
||||
*/
|
||||
public function search($pattern, $searchProperties = [], $options = []) {
|
||||
$this->loadAddressBooks();
|
||||
$result = [];
|
||||
foreach ($this->addressBooks as $addressBook) {
|
||||
$r = $addressBook->search($pattern, $searchProperties, $options);
|
||||
$searchOptions = $options;
|
||||
$strictSearch = array_key_exists('strict_search', $options) && $options['strict_search'] === true;
|
||||
|
||||
if ($addressBook->isSystemAddressBook()) {
|
||||
$fullMatch = !\array_key_exists('fullmatch', $options) || $options['fullmatch'] !== false;
|
||||
if (!$fullMatch) {
|
||||
// Neither full match is allowed, so skip the system address book
|
||||
continue;
|
||||
}
|
||||
if ($strictSearch) {
|
||||
$searchOptions['wildcard'] = false;
|
||||
} else {
|
||||
$searchOptions['wildcard'] = !\array_key_exists('enumeration', $options) || $options['enumeration'] !== false;
|
||||
}
|
||||
} else {
|
||||
$searchOptions['wildcard'] = !$strictSearch;
|
||||
}
|
||||
|
||||
$r = $addressBook->search($pattern, $searchProperties, $searchOptions);
|
||||
$contacts = [];
|
||||
foreach ($r as $c) {
|
||||
$c['addressbook-key'] = $addressBook->getKey();
|
||||
|
|
|
|||
|
|
@ -83,7 +83,12 @@ class CloudIdManager implements ICloudIdManager {
|
|||
}
|
||||
|
||||
protected function getDisplayNameFromContact(string $cloudId): ?string {
|
||||
$addressBookEntries = $this->contactsManager->search($cloudId, ['CLOUD']);
|
||||
$addressBookEntries = $this->contactsManager->search($cloudId, ['CLOUD'], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'fullmatch' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
foreach ($addressBookEntries as $entry) {
|
||||
if (isset($entry['CLOUD'])) {
|
||||
foreach ($entry['CLOUD'] as $cloudID) {
|
||||
|
|
|
|||
|
|
@ -594,7 +594,12 @@ class Share extends Constants {
|
|||
$row['share_with_displayname'] = $shareWithUser === null ? $row['share_with'] : $shareWithUser->getDisplayName();
|
||||
} elseif (isset($row['share_with']) && $row['share_with'] != '' &&
|
||||
$row['share_type'] === IShare::TYPE_REMOTE) {
|
||||
$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD']);
|
||||
$addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD'], [
|
||||
'limit' => 1,
|
||||
'enumeration' => false,
|
||||
'fullmatch' => false,
|
||||
'strict_search' => true,
|
||||
]);
|
||||
foreach ($addressBookEntries as $entry) {
|
||||
foreach ($entry['CLOUD'] as $cloudID) {
|
||||
if ($cloudID === $row['share_with']) {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,10 @@ interface IManager {
|
|||
* - 'escape_like_param' - If set to false wildcards _ and % are not escaped
|
||||
* - 'limit' - Set a numeric limit for the search results
|
||||
* - 'offset' - Set the offset for the limited search results
|
||||
* - 'enumeration' - (since 23.0.0) Whether user enumeration on system address book is allowed
|
||||
* - 'fullmatch' - (since 23.0.0) Whether matching on full detail in system addresss book is allowed
|
||||
* - 'strict_search' - (since 23.0.0) Whether the search pattern is full string or partial search
|
||||
* @psalm-param array{escape_like_param?: bool, limit?: int, offset?: int, enumeration?: bool, fullmatch?: bool, strict_search?: bool} $options
|
||||
* @return array an array of contacts which are arrays of key-value-pairs
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ namespace OCP {
|
|||
* - 'escape_like_param' - If set to false wildcards _ and % are not escaped
|
||||
* - 'limit' - Set a numeric limit for the search results
|
||||
* - 'offset' - Set the offset for the limited search results
|
||||
* - 'wildcard' - (since 23.0.0) Whether the search should use wildcards
|
||||
* @psalm-param array{types?: bool, escape_like_param?: bool, limit?: int, offset?: int, wildcard?: bool} $options
|
||||
* @return array an array of contacts which are arrays of key-value-pairs
|
||||
* example result:
|
||||
* [
|
||||
|
|
|
|||
Loading…
Reference in a new issue