Quellcode durchsuchen

Add a setting to restrict returning a full match unless in phonebook or same group

Signed-off-by: Joas Schilling <coding@schilljs.com>
tags/v22.0.0beta1
Joas Schilling vor 3 Jahren
Ursprung
Commit
5b53b6f977
Es ist kein Account mit der E-Mail-Adresse des Committers verbunden

+ 23
- 13
apps/dav/lib/Connector/Sabre/Principal.php Datei anzeigen

$allowEnumeration = $this->shareManager->allowEnumeration(); $allowEnumeration = $this->shareManager->allowEnumeration();
$limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups(); $limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups();
$limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone(); $limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone();
$allowEnumerationFullMatch = $this->shareManager->allowEnumerationFullMatch();


// If sharing is restricted to group members only, // If sharing is restricted to group members only,
// return only members that have groups in common // return only members that have groups in common
foreach ($searchProperties as $prop => $value) { foreach ($searchProperties as $prop => $value) {
switch ($prop) { switch ($prop) {
case '{http://sabredav.org/ns}email-address': case '{http://sabredav.org/ns}email-address':
$users = $this->userManager->getByEmail($value);

if (!$allowEnumeration) { if (!$allowEnumeration) {
$users = \array_filter($users, static function (IUser $user) use ($value) {
return $user->getEMailAddress() === $value;
});
if ($allowEnumerationFullMatch) {
$users = $this->userManager->getByEmail($value);
$users = \array_filter($users, static function (IUser $user) use ($value) {
return $user->getEMailAddress() === $value;
});
} else {
$users = [];
}
} else { } else {
$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $currentUserGroups) {
if ($user->getEMailAddress() === $value) {
$users = $this->userManager->getByEmail($value);
$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
if ($allowEnumerationFullMatch && $user->getEMailAddress() === $value) {
return true; return true;
} }


break; break;


case '{DAV:}displayname': case '{DAV:}displayname':
$users = $this->userManager->searchDisplayName($value, $searchLimit);


if (!$allowEnumeration) { if (!$allowEnumeration) {
$users = \array_filter($users, static function (IUser $user) use ($value) {
return $user->getDisplayName() === $value;
});
if ($allowEnumerationFullMatch) {
$users = $this->userManager->searchDisplayName($value, $searchLimit);
$users = \array_filter($users, static function (IUser $user) use ($value) {
return $user->getDisplayName() === $value;
});
} else {
$users = [];
}
} else { } else {
$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $currentUserGroups) {
if ($user->getDisplayName() === $value) {
$users = $this->userManager->searchDisplayName($value, $searchLimit);
$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $allowEnumerationFullMatch, $currentUserGroups) {
if ($allowEnumerationFullMatch && $user->getDisplayName() === $value) {
return true; return true;
} }



+ 51
- 0
apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php Datei anzeigen

->method('shareWithGroupMembersOnly') ->method('shareWithGroupMembersOnly')
->willReturn(false); ->willReturn(false);


$this->shareManager->expects($this->once())
->method('allowEnumerationFullMatch')
->willReturn(true);

$user2 = $this->createMock(IUser::class); $user2 = $this->createMock(IUser::class);
$user2->method('getUID')->willReturn('user2'); $user2->method('getUID')->willReturn('user2');
$user2->method('getDisplayName')->willReturn('User 2'); $user2->method('getDisplayName')->willReturn('User 2');
['{DAV:}displayname' => 'User 2'])); ['{DAV:}displayname' => 'User 2']));
} }


public function testSearchPrincipalWithEnumerationDisabledDisplaynameOnFullMatch() {
$this->shareManager->expects($this->once())
->method('shareAPIEnabled')
->willReturn(true);

$this->shareManager->expects($this->once())
->method('allowEnumeration')
->willReturn(false);

$this->shareManager->expects($this->once())
->method('shareWithGroupMembersOnly')
->willReturn(false);

$this->shareManager->expects($this->once())
->method('allowEnumerationFullMatch')
->willReturn(false);

$this->assertEquals([], $this->connector->searchPrincipals('principals/users',
['{DAV:}displayname' => 'User 2']));
}

public function testSearchPrincipalWithEnumerationDisabledEmail() { public function testSearchPrincipalWithEnumerationDisabledEmail() {
$this->shareManager->expects($this->once()) $this->shareManager->expects($this->once())
->method('shareAPIEnabled') ->method('shareAPIEnabled')
->method('shareWithGroupMembersOnly') ->method('shareWithGroupMembersOnly')
->willReturn(false); ->willReturn(false);


$this->shareManager->expects($this->once())
->method('allowEnumerationFullMatch')
->willReturn(true);

$user2 = $this->createMock(IUser::class); $user2 = $this->createMock(IUser::class);
$user2->method('getUID')->willReturn('user2'); $user2->method('getUID')->willReturn('user2');
$user2->method('getDisplayName')->willReturn('User 2'); $user2->method('getDisplayName')->willReturn('User 2');
['{http://sabredav.org/ns}email-address' => 'user2@foo.bar'])); ['{http://sabredav.org/ns}email-address' => 'user2@foo.bar']));
} }


public function testSearchPrincipalWithEnumerationDisabledEmailOnFullMatch() {
$this->shareManager->expects($this->once())
->method('shareAPIEnabled')
->willReturn(true);

$this->shareManager->expects($this->once())
->method('allowEnumeration')
->willReturn(false);

$this->shareManager->expects($this->once())
->method('shareWithGroupMembersOnly')
->willReturn(false);

$this->shareManager->expects($this->once())
->method('allowEnumerationFullMatch')
->willReturn(false);


$this->assertEquals([], $this->connector->searchPrincipals('principals/users',
['{http://sabredav.org/ns}email-address' => 'user2@foo.bar']));
}

public function testSearchPrincipalWithEnumerationLimitedDisplayname() { public function testSearchPrincipalWithEnumerationLimitedDisplayname() {
$this->shareManager->expects($this->at(0)) $this->shareManager->expects($this->at(0))
->method('shareAPIEnabled') ->method('shareAPIEnabled')

+ 1
- 0
apps/settings/lib/Settings/Admin/Sharing.php Datei anzeigen

'allowShareDialogUserEnumeration' => $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes'), 'allowShareDialogUserEnumeration' => $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes'),
'restrictUserEnumerationToGroup' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no'), 'restrictUserEnumerationToGroup' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no'),
'restrictUserEnumerationToPhone' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no'), 'restrictUserEnumerationToPhone' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no'),
'restrictUserEnumerationFullMatch' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes'),
'enforceLinkPassword' => Util::isPublicLinkPasswordRequired(), 'enforceLinkPassword' => Util::isPublicLinkPasswordRequired(),
'onlyShareWithGroupMembers' => $this->shareManager->shareWithGroupMembersOnly(), 'onlyShareWithGroupMembers' => $this->shareManager->shareWithGroupMembersOnly(),
'shareAPIEnabled' => $this->config->getAppValue('core', 'shareapi_enabled', 'yes'), 'shareAPIEnabled' => $this->config->getAppValue('core', 'shareapi_enabled', 'yes'),

+ 10
- 1
apps/settings/templates/settings/admin/sharing.php Datei anzeigen

<?php if ($_['allowShareDialogUserEnumeration'] === 'yes') { <?php if ($_['allowShareDialogUserEnumeration'] === 'yes') {
print_unescaped('checked="checked"'); print_unescaped('checked="checked"');
} ?> /> } ?> />
<label for="shareapi_allow_share_dialog_user_enumeration"><?php p($l->t('Allow username autocompletion in share dialog (if this is disabled the full username or email address needs to be entered)'));?></label><br />
<label for="shareapi_allow_share_dialog_user_enumeration"><?php p($l->t('Allow username autocompletion in share dialog'));?></label><br />
</p> </p>


<p id="shareapi_restrict_user_enumeration_to_group_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') { <p id="shareapi_restrict_user_enumeration_to_group_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no' || $_['allowShareDialogUserEnumeration'] === 'no') {
}?>"> }?>">
<em><?php p($l->t('If autocompletion "same group" and "phonebook matches" are enabled a match in either is enough to show the user.'));?></em><br /> <em><?php p($l->t('If autocompletion "same group" and "phonebook matches" are enabled a match in either is enough to show the user.'));?></em><br />
</p> </p>
<p id="shareapi_restrict_user_enumeration_full_match_setting" class="indent <?php if ($_['shareAPIEnabled'] === 'no') {
p('hidden');
}?>">
<input type="checkbox" name="shareapi_restrict_user_enumeration_full_match" value="1" id="shareapi_restrict_user_enumeration_full_match" class="checkbox"
<?php if ($_['restrictUserEnumerationFullMatch'] === 'yes') {
print_unescaped('checked="checked"');
} ?> />
<label for="shareapi_restrict_user_enumeration_full_match"><?php p($l->t('Allow username autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)'));?></label><br />
</p>


<p> <p>
<input type="checkbox" id="publicShareDisclaimer" class="checkbox noJSAutoUpdate" <input type="checkbox" id="publicShareDisclaimer" class="checkbox noJSAutoUpdate"

+ 4
- 0
apps/settings/tests/Settings/Admin/SharingTest.php Datei anzeigen

['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'], ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'], ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'], ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
['core', 'shareapi_enabled', 'yes', 'yes'], ['core', 'shareapi_enabled', 'yes', 'yes'],
['core', 'shareapi_default_expire_date', 'no', 'no'], ['core', 'shareapi_default_expire_date', 'no', 'no'],
['core', 'shareapi_expire_after_n_days', '7', '7'], ['core', 'shareapi_expire_after_n_days', '7', '7'],
'allowShareDialogUserEnumeration' => 'yes', 'allowShareDialogUserEnumeration' => 'yes',
'restrictUserEnumerationToGroup' => 'no', 'restrictUserEnumerationToGroup' => 'no',
'restrictUserEnumerationToPhone' => 'no', 'restrictUserEnumerationToPhone' => 'no',
'restrictUserEnumerationFullMatch' => 'yes',
'enforceLinkPassword' => false, 'enforceLinkPassword' => false,
'onlyShareWithGroupMembers' => false, 'onlyShareWithGroupMembers' => false,
'shareAPIEnabled' => 'yes', 'shareAPIEnabled' => 'yes',
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'], ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'], ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'], ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
['core', 'shareapi_enabled', 'yes', 'yes'], ['core', 'shareapi_enabled', 'yes', 'yes'],
['core', 'shareapi_default_expire_date', 'no', 'no'], ['core', 'shareapi_default_expire_date', 'no', 'no'],
['core', 'shareapi_expire_after_n_days', '7', '7'], ['core', 'shareapi_expire_after_n_days', '7', '7'],
'allowShareDialogUserEnumeration' => 'yes', 'allowShareDialogUserEnumeration' => 'yes',
'restrictUserEnumerationToGroup' => 'no', 'restrictUserEnumerationToGroup' => 'no',
'restrictUserEnumerationToPhone' => 'no', 'restrictUserEnumerationToPhone' => 'no',
'restrictUserEnumerationFullMatch' => 'yes',
'enforceLinkPassword' => false, 'enforceLinkPassword' => false,
'onlyShareWithGroupMembers' => false, 'onlyShareWithGroupMembers' => false,
'shareAPIEnabled' => 'yes', 'shareAPIEnabled' => 'yes',

+ 36
- 0
build/integration/collaboration_features/autocomplete.feature Datei anzeigen

Given using api version "2" Given using api version "2"
And group "commongroup" exists And group "commongroup" exists
And user "admin" belongs to group "commongroup" And user "admin" belongs to group "commongroup"
And user "auto" exists
And user "autocomplete" exists And user "autocomplete" exists
And user "autocomplete2" exists And user "autocomplete2" exists
And user "autocomplete2" belongs to group "commongroup" And user "autocomplete2" belongs to group "commongroup"
When parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no" When parameter "shareapi_allow_share_dialog_user_enumeration" of app "core" is set to "no"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |
Then get autocomplete for "autocomplete" Then get autocomplete for "autocomplete"
| id | source | | id | source |
| autocomplete | users | | autocomplete | users |
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
Then get autocomplete for "auto"
| id | source |
Then get autocomplete for "autocomplete"
| id | source |




Scenario: getting autocomplete with limited enumeration by group Scenario: getting autocomplete with limited enumeration by group
When parameter "shareapi_restrict_user_enumeration_to_group" of app "core" is set to "yes" When parameter "shareapi_restrict_user_enumeration_to_group" of app "core" is set to "yes"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |
| autocomplete2 | users | | autocomplete2 | users |
Then get autocomplete for "autocomplete" Then get autocomplete for "autocomplete"
| id | source | | id | source |
Then get autocomplete for "autocomplete2" Then get autocomplete for "autocomplete2"
| id | source | | id | source |
| autocomplete2 | users | | autocomplete2 | users |
When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
Then get autocomplete for "autocomplete"
| id | source |
| autocomplete2 | users |
Then get autocomplete for "autocomplete2"
| id | source |
| autocomplete2 | users |




Scenario: getting autocomplete with limited enumeration by phone Scenario: getting autocomplete with limited enumeration by phone
When parameter "shareapi_restrict_user_enumeration_to_phone" of app "core" is set to "yes" When parameter "shareapi_restrict_user_enumeration_to_phone" of app "core" is set to "yes"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |


# autocomplete stores their phone number # autocomplete stores their phone number
Given As an "autocomplete" Given As an "autocomplete"
Given As an "admin" Given As an "admin"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |


# admin populates they have the phone number # admin populates they have the phone number
When search users by phone for region "DE" with When search users by phone for region "DE" with
| random-string1 | 0711 / 252 428-90 | | random-string1 | 0711 / 252 428-90 |
Then get autocomplete for "auto"
| id | source |
| auto | users |
| autocomplete | users |

When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| autocomplete | users | | autocomplete | users |
When search users by phone for region "DE" with When search users by phone for region "DE" with
| random-string1 | 0711 / 252 428-90 | | random-string1 | 0711 / 252 428-90 |


Then get autocomplete for "auto"
| id | source |
| auto | users |
| autocomplete | users |
| autocomplete2 | users |

When parameter "shareapi_restrict_user_enumeration_full_match" of app "core" is set to "no"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| autocomplete | users | | autocomplete | users |


Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |
| autocomplete | users | | autocomplete | users |
| autocomplete2 | users | | autocomplete2 | users |
When parameter "shareapi_only_share_with_group_members" of app "core" is set to "yes" When parameter "shareapi_only_share_with_group_members" of app "core" is set to "yes"
When parameter "shareapi_restrict_user_enumeration_to_phone" of app "core" is set to "yes" When parameter "shareapi_restrict_user_enumeration_to_phone" of app "core" is set to "yes"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |


# autocomplete stores their phone number # autocomplete stores their phone number
Given As an "autocomplete" Given As an "autocomplete"
Given As an "admin" Given As an "admin"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |


# admin populates they have the phone number # admin populates they have the phone number
When search users by phone for region "DE" with When search users by phone for region "DE" with
| random-string1 | 0711 / 252 428-90 | | random-string1 | 0711 / 252 428-90 |
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |
| autocomplete | users | | autocomplete | users |


# autocomplete changes their phone number # autocomplete changes their phone number
Given As an "admin" Given As an "admin"
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |


# admin populates they have the new phone number # admin populates they have the new phone number
When search users by phone for region "DE" with When search users by phone for region "DE" with
| random-string1 | 0711 / 252 428-91 | | random-string1 | 0711 / 252 428-91 |
Then get autocomplete for "auto" Then get autocomplete for "auto"
| id | source | | id | source |
| auto | users |
| autocomplete | users | | autocomplete | users |





+ 1
- 0
build/integration/features/bootstrap/CollaborationContext.php Datei anzeigen

$this->deleteServerConfig('core', 'shareapi_allow_share_dialog_user_enumeration'); $this->deleteServerConfig('core', 'shareapi_allow_share_dialog_user_enumeration');
$this->deleteServerConfig('core', 'shareapi_restrict_user_enumeration_to_group'); $this->deleteServerConfig('core', 'shareapi_restrict_user_enumeration_to_group');
$this->deleteServerConfig('core', 'shareapi_restrict_user_enumeration_to_phone'); $this->deleteServerConfig('core', 'shareapi_restrict_user_enumeration_to_phone');
$this->deleteServerConfig('core', 'shareapi_restrict_user_enumeration_full_match');
$this->deleteServerConfig('core', 'shareapi_only_share_with_group_members'); $this->deleteServerConfig('core', 'shareapi_only_share_with_group_members');
} }
} }

+ 4
- 1
lib/private/Collaboration/Collaborators/MailPlugin.php Datei anzeigen

protected $shareeEnumerationInGroupOnly; protected $shareeEnumerationInGroupOnly;
/* @var bool */ /* @var bool */
protected $shareeEnumerationPhone; protected $shareeEnumerationPhone;
/* @var bool */
protected $shareeEnumerationFullMatch;


/** @var IManager */ /** @var IManager */
private $contactsManager; private $contactsManager;
$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
$this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
$this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
$this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
} }


/** /**
continue; continue;
} }
} }
if ($exactEmailMatch) {
if ($exactEmailMatch && $this->shareeEnumerationFullMatch) {
try { try {
$cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]);
} catch (\InvalidArgumentException $e) { } catch (\InvalidArgumentException $e) {

+ 5
- 1
lib/private/Collaboration/Collaborators/UserPlugin.php Datei anzeigen

protected $shareeEnumerationInGroupOnly; protected $shareeEnumerationInGroupOnly;
/* @var bool */ /* @var bool */
protected $shareeEnumerationPhone; protected $shareeEnumerationPhone;
/* @var bool */
protected $shareeEnumerationFullMatch;


/** @var IConfig */ /** @var IConfig */
private $config; private $config;
$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
$this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
$this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
$this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
} }


public function search($search, $limit, $offset, ISearchResult $searchResult) { public function search($search, $limit, $offset, ISearchResult $searchResult) {




if ( if (
$this->shareeEnumerationFullMatch &&
$lowerSearch !== '' && (strtolower($uid) === $lowerSearch || $lowerSearch !== '' && (strtolower($uid) === $lowerSearch ||
strtolower($userDisplayName) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch ||
strtolower($userEmail) === $lowerSearch) strtolower($userEmail) === $lowerSearch)
} }
} }


if ($offset === 0 && !$foundUserById) {
if ($this->shareeEnumerationFullMatch && $offset === 0 && !$foundUserById) {
// On page one we try if the search result has a direct hit on the // On page one we try if the search result has a direct hit on the
// user id and if so, we add that to the exact match list // user id and if so, we add that to the exact match list
$user = $this->userManager->get($search); $user = $this->userManager->get($search);

+ 6
- 1
lib/private/Contacts/ContactsMenu/ContactsStore.php Datei anzeigen

$disallowEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') !== 'yes'; $disallowEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') !== 'yes';
$restrictEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $restrictEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
$restrictEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; $restrictEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
$allowEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes'; $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes';


// whether to filter out local users // whether to filter out local users


$selfUID = $self->getUID(); $selfUID = $self->getUID();


return array_values(array_filter($entries, function (IEntry $entry) use ($skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $restrictEnumerationGroup, $restrictEnumerationPhone, $filter) {
return array_values(array_filter($entries, function (IEntry $entry) use ($skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $restrictEnumerationGroup, $restrictEnumerationPhone, $allowEnumerationFullMatch, $filter) {
if ($entry->getProperty('UID') === $selfUID) { if ($entry->getProperty('UID') === $selfUID) {
return false; return false;
} }


// Prevent enumerating local users // Prevent enumerating local users
if ($disallowEnumeration) { if ($disallowEnumeration) {
if (!$allowEnumerationFullMatch) {
return false;
}

$filterUser = true; $filterUser = true;


$mailAddresses = $entry->getEMailAddresses(); $mailAddresses = $entry->getEMailAddresses();

+ 4
- 0
lib/private/Share20/Manager.php Datei anzeigen

$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
} }


public function allowEnumerationFullMatch(): bool {
return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
}

/** /**
* Copied from \OC_Util::isSharingDisabledForUser * Copied from \OC_Util::isSharingDisabledForUser
* *

+ 8
- 0
lib/public/Share/IManager.php Datei anzeigen

*/ */
public function limitEnumerationToPhone(): bool; public function limitEnumerationToPhone(): bool;


/**
* Check if user enumeration is allowed to return on full match
*
* @return bool
* @since 21.0.1
*/
public function allowEnumerationFullMatch(): bool;

/** /**
* Check if sharing is disabled for the given user * Check if sharing is disabled for the given user
* *

+ 90
- 3
tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php Datei anzeigen

} }


public function testGetContactsWithFilter() { public function testGetContactsWithFilter() {
$this->config->expects($this->at(0))->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
->willReturn('no');
$this->config
->method('getAppValue')
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
]);


/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
$user = $this->createMock(IUser::class); $user = $this->createMock(IUser::class);
], $entry[0]->getEMailAddresses()); ], $entry[0]->getEMailAddresses());
} }


public function testGetContactsWithFilterWithoutFullMatch() {
$this->config
->method('getAppValue')
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'no'],
]);

/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
$user = $this->createMock(IUser::class);
$this->contactsManager->expects($this->any())
->method('search')
->willReturn([
[
'UID' => 'a567',
'FN' => 'Darren Roner',
'EMAIL' => [
'darren@roner.au',
],
'isLocalSystemBook' => true,
],
[
'UID' => 'john',
'FN' => 'John Doe',
'EMAIL' => [
'john@example.com',
],
'isLocalSystemBook' => true,
],
[
'FN' => 'Anne D',
'EMAIL' => [
'anne@example.com',
],
'isLocalSystemBook' => false,
],
]);
$user->expects($this->any())
->method('getUID')
->willReturn('user123');

// Complete match on UID should not match
$entry = $this->contactsStore->getContacts($user, 'a567');
$this->assertSame(1, count($entry));
$this->assertEquals([
'anne@example.com'
], $entry[0]->getEMailAddresses());

// Partial match on UID should not match
$entry = $this->contactsStore->getContacts($user, 'a56');
$this->assertSame(1, count($entry));
$this->assertEquals([
'anne@example.com'
], $entry[0]->getEMailAddresses());

// Complete match on email should not match
$entry = $this->contactsStore->getContacts($user, 'john@example.com');
$this->assertSame(1, count($entry));
$this->assertEquals([
'anne@example.com'
], $entry[0]->getEMailAddresses());

// Partial match on email should not match
$entry = $this->contactsStore->getContacts($user, 'john@example.co');
$this->assertSame(1, count($entry));
$this->assertEquals([
'anne@example.com'
], $entry[0]->getEMailAddresses());

// Match on FN should not match
$entry = $this->contactsStore->getContacts($user, 'Darren Roner');
$this->assertSame(1, count($entry));
$this->assertEquals([
'anne@example.com'
], $entry[0]->getEMailAddresses());

// Don't filter users in local addressbook
$entry = $this->contactsStore->getContacts($user, 'Anne D');
$this->assertSame(1, count($entry));
$this->assertEquals([
'anne@example.com'
], $entry[0]->getEMailAddresses());
}

public function testFindOneUser() { public function testFindOneUser() {
$this->config->expects($this->at(0))->method('getAppValue') $this->config->expects($this->at(0))->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes')) ->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))

Laden…
Abbrechen
Speichern