Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/settings/lib/Settings/Admin/Sharing.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ public function getForm() {
'allowFederationOnPublicShares' => $this->appConfig->getValueBool('core', ConfigLexicon::SHAREAPI_ALLOW_FEDERATION_ON_PUBLIC_SHARES),
'restrictUserEnumerationToGroup' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_group'),
'restrictUserEnumerationToPhone' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_to_phone'),
'restrictUserEnumerationFullMatch' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match', true),
'restrictUserEnumerationFullMatchUserId' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_userid', true),
'restrictUserEnumerationFullMatchEmail' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_email', true),
'restrictUserEnumerationFullMatchIgnoreSecondDN' => $this->getHumanBooleanConfig('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn'),
'restrictUserEnumerationFullMatch' => $this->shareManager->allowEnumerationFullMatch(),
'restrictUserEnumerationFullMatchUserId' => $this->shareManager->matchUserId(),
'restrictUserEnumerationFullMatchEmail' => $this->shareManager->matchEmail(),
'restrictUserEnumerationFullMatchIgnoreSecondDN' => $this->shareManager->ignoreSecondDisplayName(),
'enforceLinksPassword' => Util::isPublicLinkPasswordRequired(false),
'enforceLinksPasswordExcludedGroups' => json_decode($excludedPasswordGroups) ?? [],
'enforceLinksPasswordExcludedGroupsEnabled' => $this->config->getSystemValueBool('sharing.allow_disabled_password_enforcement_groups', false),
Expand Down
37 changes: 28 additions & 9 deletions apps/settings/src/components/AdminSettingsSharingForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
-->
<template>
<form class="sharing">
<NcCheckboxRadioSwitch aria-controls="settings-sharing-api settings-sharing-api-settings settings-sharing-default-permissions settings-sharing-privary-related"
<NcCheckboxRadioSwitch aria-controls="settings-sharing-api settings-sharing-api-settings settings-sharing-default-permissions settings-sharing-privacy-related"
type="switch"
:checked.sync="settings.enabled">
{{ t('settings', 'Allow apps to use the Share API') }}
Expand Down Expand Up @@ -174,7 +174,7 @@
</fieldset>
</div>

<div v-show="settings.enabled" id="settings-sharing-privary-related" class="sharing__section">
<div v-show="settings.enabled" id="settings-sharing-privacy-related" class="sharing__section">
<h3>{{ t('settings', 'Privacy settings for sharing') }}</h3>

<NcCheckboxRadioSwitch type="switch"
Expand All @@ -183,33 +183,52 @@
{{ t('settings', 'Allow account name autocompletion in share dialog and allow access to the system address book') }}
</NcCheckboxRadioSwitch>
<fieldset v-show="settings.allowShareDialogUserEnumeration" id="settings-sharing-privacy-user-enumeration" class="sharing__sub-section">
<legend class="hidden-visually">
{{ t('settings', 'Sharing autocompletion restrictions') }}
</legend>
<em>
{{ t('settings', 'If autocompletion "same group" and "phone number integration" are enabled a match in either is enough to show the user.') }}
{{ t('settings', 'If autocompletion restrictions for both "same group" and "phonebook integration" are enabled, a match in either is enough to show the user.') }}
</em>
<NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationToGroup">
{{ t('settings', 'Restrict account name autocompletion and system address book access to users within the same groups') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationToPhone">
{{ t('settings', 'Restrict account name autocompletion to users based on phone number integration') }}
{{ t('settings', 'Restrict account name autocompletion to users based on their phonebook') }}
</NcCheckboxRadioSwitch>
</fieldset>

<NcCheckboxRadioSwitch type="switch" :checked.sync="settings.restrictUserEnumerationFullMatch">
{{ t('settings', 'Allow autocompletion when entering the full name or email address (ignoring missing phonebook match and being in the same group)') }}
<NcCheckboxRadioSwitch v-model="settings.restrictUserEnumerationFullMatch"
type="switch"
aria-controls="settings-sharing-privacy-autocomplete">
{{ t('settings', 'Allow autocompletion to full match when entering the full name (ignoring restrictions like group membership or missing phonebook match)') }}
</NcCheckboxRadioSwitch>
<fieldset v-show="settings.restrictUserEnumerationFullMatch" id="settings-sharing-privacy-autocomplete" class="sharing__sub-section">
<legend class="hidden-visually">
{{ t('settings', 'Full match autocompletion restrictions') }}
</legend>
<NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationFullMatchUserId">
{{ t('settings', 'Also allow autocompletion on full match of the user id') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationFullMatchEmail">
{{ t('settings', 'Also allow autocompletion on full match of the user email') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch :checked.sync="settings.restrictUserEnumerationFullMatchIgnoreSecondDN">
{{ t('settings', 'Do not use second user displayname for full match') }}
</NcCheckboxRadioSwitch>
</fieldset>

<NcCheckboxRadioSwitch type="switch" :checked.sync="publicShareDisclaimerEnabled">
{{ t('settings', 'Show disclaimer text on the public link upload page (only shown when the file list is hidden)') }}
</NcCheckboxRadioSwitch>
<div v-if="publicShareDisclaimerEnabled"
aria-describedby="settings-sharing-privary-related-disclaimer-hint"
aria-describedby="settings-sharing-privacy-related-disclaimer-hint"
class="sharing__sub-section">
<NcTextArea class="sharing__input"
:label="t('settings', 'Disclaimer text')"
aria-describedby="settings-sharing-privary-related-disclaimer-hint"
aria-describedby="settings-sharing-privacy-related-disclaimer-hint"
:value="settings.publicShareDisclaimerText"
@update:value="onUpdateDisclaimer" />
<em id="settings-sharing-privary-related-disclaimer-hint" class="sharing__input">
<em id="settings-sharing-privacy-related-disclaimer-hint" class="sharing__input">
{{ t('settings', 'This text will be shown on the public link upload page when the file list is hidden.') }}
</em>
</div>
Expand Down
69 changes: 40 additions & 29 deletions lib/private/Collaboration/Collaborators/UserPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
$users = [];
$hasMoreResults = false;

$currentUserId = $this->userSession->getUser()->getUID();
$currentUserGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
/** @var IUser */
$currentUser = $this->userSession->getUser();
$currentUserId = $currentUser->getUID();
$currentUserGroups = $this->groupManager->getUserGroupIds($currentUser);

// ShareWithGroupOnly filtering
$currentUserGroups = array_diff($currentUserGroups, $this->shareWithGroupOnlyExcludeGroupsList);
Expand All @@ -76,7 +78,7 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
foreach ($usersInGroup as $userId => $displayName) {
$userId = (string)$userId;
$user = $this->userManager->get($userId);
if (!$user->isEnabled()) {
if (!$user?->isEnabled()) {
// Ignore disabled users
continue;
}
Expand All @@ -86,37 +88,43 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
$hasMoreResults = true;
}
}
}

if (!$this->shareWithGroupOnly && $this->shareeEnumerationPhone) {
$usersTmp = $this->userManager->searchKnownUsersByDisplayName($currentUserId, $search, $limit, $offset);
if (!empty($usersTmp)) {
// not limited to group only sharing
if (!$this->shareWithGroupOnly) {
if (!$this->shareeEnumerationPhone && !$this->shareeEnumerationInGroupOnly) {
// no restrictions, add everything
$usersTmp = $this->userManager->searchDisplayName($search, $limit, $offset);
foreach ($usersTmp as $user) {
if ($user->isEnabled()) { // Don't keep deactivated users
$users[$user->getUID()] = $user;
}
}
} else {
// make sure to add phonebook matches if configured
if ($this->shareeEnumerationPhone) {
$usersTmp = $this->userManager->searchKnownUsersByDisplayName($currentUserId, $search, $limit, $offset);
foreach ($usersTmp as $user) {
if ($user->isEnabled()) { // Don't keep deactivated users
$users[$user->getUID()] = $user;
}
}

uasort($users, function ($a, $b) {
/**
* @var \OC\User\User $a
* @var \OC\User\User $b
*/
return strcasecmp($a->getDisplayName(), $b->getDisplayName());
});
}
}
} else {
// Search in all users
if ($this->shareeEnumerationPhone) {
$usersTmp = $this->userManager->searchKnownUsersByDisplayName($currentUserId, $search, $limit, $offset);
} else {
$usersTmp = $this->userManager->searchDisplayName($search, $limit, $offset);
}
foreach ($usersTmp as $user) {
if ($user->isEnabled()) { // Don't keep deactivated users
$users[$user->getUID()] = $user;

// additionally we need to add full matches
if ($this->shareeEnumerationFullMatch) {
$usersTmp = $this->userManager->searchDisplayName($search, $limit, $offset);
foreach ($usersTmp as $user) {
if ($user->isEnabled() && mb_strtolower($user->getDisplayName()) === mb_strtolower($search)) {
$users[$user->getUID()] = $user;
}
}
}
}

uasort($users, function (IUser $a, IUser $b) {
return strcasecmp($a->getDisplayName(), $b->getDisplayName());
});
}

$this->takeOutCurrentUser($users);
Expand Down Expand Up @@ -149,10 +157,13 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b

if (
$this->shareeEnumerationFullMatch
&& $lowerSearch !== '' && (strtolower($uid) === $lowerSearch
|| strtolower($userDisplayName) === $lowerSearch
|| ($this->shareeEnumerationFullMatchIgnoreSecondDisplayName && trim(strtolower(preg_replace('/ \(.*\)$/', '', $userDisplayName))) === $lowerSearch)
|| ($this->shareeEnumerationFullMatchEmail && strtolower($userEmail ?? '') === $lowerSearch))
&& $lowerSearch !== ''
&& (
strtolower($uid) === $lowerSearch
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if strcasecmp is better but it seems similar :)

|| strtolower($userDisplayName) === $lowerSearch
|| ($this->shareeEnumerationFullMatchIgnoreSecondDisplayName && trim(strtolower(preg_replace('/ \(.*\)$/', '', $userDisplayName))) === $lowerSearch)
|| ($this->shareeEnumerationFullMatchEmail && strtolower($userEmail ?? '') === $lowerSearch)
)
) {
if (strtolower($uid) === $lowerSearch) {
$foundUserById = true;
Expand Down
4 changes: 4 additions & 0 deletions lib/private/Share20/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -1931,6 +1931,10 @@ public function matchEmail(): bool {
return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
}

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

public function ignoreSecondDisplayName(): bool {
return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
}
Expand Down
18 changes: 15 additions & 3 deletions lib/public/Share/IManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -441,23 +441,35 @@ public function limitEnumerationToGroups(): bool;
public function limitEnumerationToPhone(): bool;

/**
* Check if user enumeration is allowed to return on full match
* Check if user enumeration is allowed to return also on full match
* and ignore limitations to phonebook or groups.
*
* @return bool
* @since 21.0.1
*/
public function allowEnumerationFullMatch(): bool;

/**
* Check if the search should match the email
* When `allowEnumerationFullMatch` is enabled and `matchEmail` is set,
* then also return results for full email matches.
*
* @return bool
* @since 25.0.0
*/
public function matchEmail(): bool;

/**
* Check if the search should ignore the second in parentheses display name if there is any
* When `allowEnumerationFullMatch` is enabled and `matchUserId` is set,
* then also return results for full user id matches.
*
* @return bool
* @since 33.0.0
*/
public function matchUserId(): bool;

/**
* When `allowEnumerationFullMatch` is enabled and `ignoreSecondDisplayName` is set,
* then the search should ignore matches on the second displayname and only use the first.
*
* @return bool
* @since 25.0.0
Expand Down
Loading