Restrict autocompletion also based on the phonebook known users

Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Joas Schilling 2021-03-09 21:48:48 +01:00
parent 52d43bf3cc
commit c7560ab3a9
No known key found for this signature in database
GPG Key ID: 7076EA9751AACDDA
18 changed files with 564 additions and 148 deletions

View File

@ -27,6 +27,7 @@
*/
// Backends
use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\Connector\LegacyDAVACL;
use OCA\DAV\CalDAV\CalendarRoot;
@ -50,6 +51,7 @@ $principalBackend = new Principal(
\OC::$server->getUserSession(),
\OC::$server->getAppManager(),
\OC::$server->query(\OCA\DAV\CalDAV\Proxy\ProxyMapper::class),
\OC::$server->get(KnownUserService::class),
\OC::$server->getConfig(),
'principals/'
);

View File

@ -27,6 +27,7 @@
*/
// Backends
use OC\KnownUser\KnownUserService;
use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\CardDAV\AddressBookRoot;
use OCA\DAV\CardDAV\CardDavBackend;
@ -53,6 +54,7 @@ $principalBackend = new Principal(
\OC::$server->getUserSession(),
\OC::$server->getAppManager(),
\OC::$server->query(\OCA\DAV\CalDAV\Proxy\ProxyMapper::class),
\OC::$server->get(KnownUserService::class),
\OC::$server->getConfig(),
'principals/'
);

View File

@ -43,8 +43,9 @@ class SystemAddressbook extends AddressBook {
public function getChildren() {
$shareEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
$restrictShareEnumeration = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
if (!$shareEnumeration || ($shareEnumeration && $restrictShareEnumeration)) {
$shareEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
$shareEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
if (!$shareEnumeration || $shareEnumerationGroup || $shareEnumerationPhone) {
return [];
}

View File

@ -27,6 +27,7 @@
namespace OCA\DAV\Command;
use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\Connector\Sabre\Principal;
@ -86,6 +87,7 @@ class CreateCalendar extends Command {
\OC::$server->getUserSession(),
\OC::$server->getAppManager(),
\OC::$server->query(ProxyMapper::class),
\OC::$server->get(KnownUserService::class),
\OC::$server->getConfig()
);
$random = \OC::$server->getSecureRandom();

View File

@ -36,6 +36,7 @@
namespace OCA\DAV\Connector\Sabre;
use OC\KnownUser\KnownUserService;
use OCA\Circles\Exceptions\CircleDoesNotExistException;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\Traits\PrincipalProxyTrait;
@ -82,27 +83,19 @@ class Principal implements BackendInterface {
/** @var ProxyMapper */
private $proxyMapper;
/** @var KnownUserService */
private $knownUserService;
/** @var IConfig */
private $config;
/**
* Principal constructor.
*
* @param IUserManager $userManager
* @param IGroupManager $groupManager
* @param IShareManager $shareManager
* @param IUserSession $userSession
* @param IAppManager $appManager
* @param ProxyMapper $proxyMapper
* @param IConfig $config
* @param string $principalPrefix
*/
public function __construct(IUserManager $userManager,
IGroupManager $groupManager,
IShareManager $shareManager,
IUserSession $userSession,
IAppManager $appManager,
ProxyMapper $proxyMapper,
KnownUserService $knownUserService,
IConfig $config,
string $principalPrefix = 'principals/users/') {
$this->userManager = $userManager;
@ -113,6 +106,7 @@ class Principal implements BackendInterface {
$this->principalPrefix = trim($principalPrefix, '/');
$this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
$this->proxyMapper = $proxyMapper;
$this->knownUserService = $knownUserService;
$this->config = $config;
}
@ -267,24 +261,24 @@ class Principal implements BackendInterface {
}
$allowEnumeration = $this->shareManager->allowEnumeration();
$limitEnumeration = $this->shareManager->limitEnumerationToGroups();
$limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups();
$limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone();
// If sharing is restricted to group members only,
// return only members that have groups in common
$restrictGroups = false;
$currentUser = $this->userSession->getUser();
if ($this->shareManager->shareWithGroupMembersOnly()) {
$user = $this->userSession->getUser();
if (!$user) {
if (!$currentUser instanceof IUser) {
return [];
}
$restrictGroups = $this->groupManager->getUserGroupIds($user);
$restrictGroups = $this->groupManager->getUserGroupIds($currentUser);
}
$currentUserGroups = [];
if ($limitEnumeration) {
$currentUser = $this->userSession->getUser();
if ($currentUser) {
if ($limitEnumerationGroup) {
if ($currentUser instanceof IUser) {
$currentUserGroups = $this->groupManager->getUserGroupIds($currentUser);
}
}
@ -302,14 +296,28 @@ class Principal implements BackendInterface {
$users = \array_filter($users, static function (IUser $user) use ($value) {
return $user->getEMailAddress() === $value;
});
}
} else {
$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $currentUserGroups) {
if ($user->getEMailAddress() === $value) {
return true;
}
if ($limitEnumeration) {
$users = \array_filter($users, function (IUser $user) use ($currentUserGroups, $value) {
return !empty(array_intersect(
$this->groupManager->getUserGroupIds($user),
$currentUserGroups
)) || $user->getEMailAddress() === $value;
if ($limitEnumerationPhone
&& $currentUser instanceof IUser
&& $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
// Synced phonebook match
return true;
}
if (!$limitEnumerationGroup) {
// No limitation on enumeration, all allowed
return true;
}
return !empty($currentUserGroups) && !empty(array_intersect(
$this->groupManager->getUserGroupIds($user),
$currentUserGroups
));
});
}
@ -334,14 +342,28 @@ class Principal implements BackendInterface {
$users = \array_filter($users, static function (IUser $user) use ($value) {
return $user->getDisplayName() === $value;
});
}
} else {
$users = \array_filter($users, function (IUser $user) use ($currentUser, $value, $limitEnumerationPhone, $limitEnumerationGroup, $currentUserGroups) {
if ($user->getDisplayName() === $value) {
return true;
}
if ($limitEnumeration) {
$users = \array_filter($users, function (IUser $user) use ($currentUserGroups, $value) {
return !empty(array_intersect(
$this->groupManager->getUserGroupIds($user),
$currentUserGroups
)) || $user->getDisplayName() === $value;
if ($limitEnumerationPhone
&& $currentUser instanceof IUser
&& $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
// Synced phonebook match
return true;
}
if (!$limitEnumerationGroup) {
// No limitation on enumeration, all allowed
return true;
}
return !empty($currentUserGroups) && !empty(array_intersect(
$this->groupManager->getUserGroupIds($user),
$currentUserGroups
));
});
}

View File

@ -28,6 +28,7 @@
namespace OCA\DAV;
use OC\KnownUser\KnownUserService;
use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarRoot;
@ -70,6 +71,7 @@ class RootCollection extends SimpleCollection {
\OC::$server->getUserSession(),
\OC::$server->getAppManager(),
$proxyMapper,
\OC::$server->get(KnownUserService::class),
\OC::$server->getConfig()
);
$groupPrincipalBackend = new GroupPrincipalBackend($groupManager, $userSession, $shareManager, $config);

View File

@ -27,6 +27,7 @@
namespace OCA\DAV\Tests\unit\CalDAV;
use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\Connector\Sabre\Principal;
@ -92,6 +93,7 @@ abstract class AbstractCalDavBackend extends TestCase {
$this->createMock(IUserSession::class),
$this->createMock(IAppManager::class),
$this->createMock(ProxyMapper::class),
$this->createMock(KnownUserService::class),
$this->createMock(IConfig::class),
])
->setMethods(['getPrincipalByPath', 'getGroupMembership'])

View File

@ -33,6 +33,7 @@
namespace OCA\DAV\Tests\unit\CardDAV;
use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\CardDAV\AddressBook;
use OCA\DAV\CardDAV\CardDavBackend;
@ -139,6 +140,7 @@ class CardDavBackendTest extends TestCase {
$this->createMock(IUserSession::class),
$this->createMock(IAppManager::class),
$this->createMock(ProxyMapper::class),
$this->createMock(KnownUserService::class),
$this->createMock(IConfig::class),
])
->setMethods(['getPrincipalByPath', 'getGroupMembership'])

View File

@ -30,6 +30,7 @@
namespace OCA\DAV\Tests\unit\Connector\Sabre;
use OC\KnownUser\KnownUserService;
use OC\User\User;
use OCA\DAV\CalDAV\Proxy\Proxy;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
@ -41,6 +42,7 @@ use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\Share\IManager;
use PHPUnit\Framework\MockObject\MockObject;
use Sabre\DAV\PropPatch;
use Test\TestCase;
@ -67,6 +69,8 @@ class PrincipalTest extends TestCase {
/** @var ProxyMapper | \PHPUnit\Framework\MockObject\MockObject */
private $proxyMapper;
/** @var KnownUserService|MockObject */
private $knownUserService;
/** @var IConfig | \PHPUnit\Framework\MockObject\MockObject */
private $config;
@ -77,6 +81,7 @@ class PrincipalTest extends TestCase {
$this->userSession = $this->createMock(IUserSession::class);
$this->appManager = $this->createMock(IAppManager::class);
$this->proxyMapper = $this->createMock(ProxyMapper::class);
$this->knownUserService = $this->createMock(KnownUserService::class);
$this->config = $this->createMock(IConfig::class);
$this->connector = new \OCA\DAV\Connector\Sabre\Principal(
@ -86,6 +91,7 @@ class PrincipalTest extends TestCase {
$this->userSession,
$this->appManager,
$this->proxyMapper,
$this->knownUserService,
$this->config
);
parent::setUp();
@ -442,7 +448,7 @@ class PrincipalTest extends TestCase {
if ($groupsOnly) {
$user = $this->createMock(IUser::class);
$this->userSession->expects($this->once())
$this->userSession->expects($this->atLeastOnce())
->method('getUser')
->willReturn($user);

View File

@ -27,6 +27,7 @@
namespace OCA\Files_Versions\AppInfo;
use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
@ -72,6 +73,7 @@ class Application extends App implements IBootstrap {
$server->getUserSession(),
$server->getAppManager(),
$server->get(ProxyMapper::class),
$server->get(KnownUserService::class),
$server->getConfig()
);
});

View File

@ -27,6 +27,7 @@
namespace OC\Collaboration\Collaborators;
use OC\KnownUser\KnownUserService;
use OCP\Collaboration\Collaborators\ISearchPlugin;
use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\Collaboration\Collaborators\SearchResultType;
@ -40,8 +41,14 @@ use OCP\IUserSession;
use OCP\Share\IShare;
class MailPlugin implements ISearchPlugin {
protected $shareeEnumeration;
/* @var bool */
protected $shareWithGroupOnly;
/* @var bool */
protected $shareeEnumeration;
/* @var bool */
protected $shareeEnumerationInGroupOnly;
/* @var bool */
protected $shareeEnumerationPhone;
/** @var IManager */
private $contactsManager;
@ -52,20 +59,28 @@ class MailPlugin implements ISearchPlugin {
/** @var IGroupManager */
private $groupManager;
/** @var KnownUserService */
private $knownUserService;
/** @var IUserSession */
private $userSession;
public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config, IGroupManager $groupManager, IUserSession $userSession) {
public function __construct(IManager $contactsManager,
ICloudIdManager $cloudIdManager,
IConfig $config,
IGroupManager $groupManager,
KnownUserService $knownUserService,
IUserSession $userSession) {
$this->contactsManager = $contactsManager;
$this->cloudIdManager = $cloudIdManager;
$this->config = $config;
$this->groupManager = $groupManager;
$this->knownUserService = $knownUserService;
$this->userSession = $userSession;
$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === '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->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
}
/**
@ -77,6 +92,8 @@ class MailPlugin implements ISearchPlugin {
* @since 13.0.0
*/
public function search($search, $limit, $offset, ISearchResult $searchResult) {
$currentUserId = $this->userSession->getUser()->getUID();
$result = $userResults = ['wide' => [], 'exact' => []];
$userType = new SearchResultType('users');
$emailType = new SearchResultType('emails');
@ -152,8 +169,12 @@ class MailPlugin implements ISearchPlugin {
continue;
}
$addToWide = !$this->shareeEnumerationInGroupOnly;
if ($this->shareeEnumerationInGroupOnly) {
$addToWide = !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone);
if (!$addToWide && $this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $contact['UID'])) {
$addToWide = true;
}
if (!$addToWide && $this->shareeEnumerationInGroupOnly) {
$addToWide = false;
$userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
foreach ($userGroups as $userGroup) {
@ -181,7 +202,7 @@ class MailPlugin implements ISearchPlugin {
}
if ($exactEmailMatch
|| isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch) {
|| (isset($contact['FN']) && strtolower($contact['FN']) === $lowerSearch)) {
if ($exactEmailMatch) {
$searchResult->markExactIdMatch($emailType);
}

View File

@ -32,6 +32,7 @@
namespace OC\Collaboration\Collaborators;
use OC\KnownUser\KnownUserService;
use OCP\Collaboration\Collaborators\ISearchPlugin;
use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\Collaboration\Collaborators\SearchResultType;
@ -46,8 +47,12 @@ use OCP\UserStatus\IManager as IUserStatusManager;
class UserPlugin implements ISearchPlugin {
/* @var bool */
protected $shareWithGroupOnly;
/* @var bool */
protected $shareeEnumeration;
/* @var bool */
protected $shareeEnumerationInGroupOnly;
/* @var bool */
protected $shareeEnumerationPhone;
/** @var IConfig */
private $config;
@ -57,33 +62,29 @@ class UserPlugin implements ISearchPlugin {
private $userSession;
/** @var IUserManager */
private $userManager;
/** @var KnownUserService */
private $knownUserService;
/** @var IUserStatusManager */
private $userStatusManager;
/**
* UserPlugin constructor.
*
* @param IConfig $config
* @param IUserManager $userManager
* @param IGroupManager $groupManager
* @param IUserSession $userSession
* @param IUserStatusManager $userStatusManager
*/
public function __construct(IConfig $config,
IUserManager $userManager,
IGroupManager $groupManager,
IUserSession $userSession,
KnownUserService $knownUserService,
IUserStatusManager $userStatusManager) {
$this->config = $config;
$this->groupManager = $groupManager;
$this->userSession = $userSession;
$this->userManager = $userManager;
$this->knownUserService = $knownUserService;
$this->userStatusManager = $userStatusManager;
$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === '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->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
}
public function search($search, $limit, $offset, ISearchResult $searchResult) {
@ -91,6 +92,7 @@ class UserPlugin implements ISearchPlugin {
$users = [];
$hasMoreResults = false;
$currentUserId = $this->userSession->getUser()->getUID();
$currentUserGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
if ($this->shareWithGroupOnly) {
// Search in all the groups this user is part of
@ -168,11 +170,16 @@ class UserPlugin implements ISearchPlugin {
];
} else {
$addToWideResults = false;
if ($this->shareeEnumeration && !$this->shareeEnumerationInGroupOnly) {
if ($this->shareeEnumeration &&
!($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone)) {
$addToWideResults = true;
}
if ($this->shareeEnumerationInGroupOnly) {
if ($this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $user->getUID())) {
$addToWideResults = true;
}
if (!$addToWideResults && $this->shareeEnumerationInGroupOnly) {
$commonGroups = array_intersect($currentUserGroups, $this->groupManager->getUserGroupIds($user));
if (!empty($commonGroups)) {
$addToWideResults = true;

View File

@ -31,6 +31,7 @@
namespace OC\Contacts\ContactsMenu;
use OC\KnownUser\KnownUserService;
use OCP\Contacts\ContactsMenu\IContactsStore;
use OCP\Contacts\ContactsMenu\IEntry;
use OCP\Contacts\IManager;
@ -53,20 +54,19 @@ class ContactsStore implements IContactsStore {
/** @var IGroupManager */
private $groupManager;
/**
* @param IManager $contactsManager
* @param IConfig $config
* @param IUserManager $userManager
* @param IGroupManager $groupManager
*/
/** @var KnownUserService */
private $knownUserService;
public function __construct(IManager $contactsManager,
IConfig $config,
IUserManager $userManager,
IGroupManager $groupManager) {
IGroupManager $groupManager,
KnownUserService $knownUserService) {
$this->contactsManager = $contactsManager;
$this->config = $config;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->knownUserService = $knownUserService;
}
/**
@ -103,7 +103,7 @@ class ContactsStore implements IContactsStore {
}
/**
* Filters the contacts. Applies 3 filters:
* Filters the contacts. Applied filters:
* 1. filter the current user
* 2. if the `shareapi_allow_share_dialog_user_enumeration` config option is
* enabled it will filter all local users
@ -122,20 +122,21 @@ class ContactsStore implements IContactsStore {
array $entries,
$filter) {
$disallowEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') !== 'yes';
$restrictEnumeration = $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';
$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes';
// whether to filter out local users
$skipLocal = false;
// whether to filter out all users which doesn't have the same group as the current user
$ownGroupsOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes' || $restrictEnumeration;
// whether to filter out all users which don't have a common group as the current user
$ownGroupsOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
$selfGroups = $this->groupManager->getUserGroupIds($self);
if ($excludedGroups) {
$excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
$decodedExcludeGroups = json_decode($excludedGroups, true);
$excludeGroupsList = ($decodedExcludeGroups !== null) ? $decodedExcludeGroups : [];
$excludeGroupsList = $decodedExcludeGroups ?? [];
if (count(array_intersect($excludeGroupsList, $selfGroups)) !== 0) {
// a group of the current user is excluded -> filter all local users
@ -145,47 +146,76 @@ class ContactsStore implements IContactsStore {
$selfUID = $self->getUID();
return array_values(array_filter($entries, function (IEntry $entry) use ($self, $skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $filter) {
if ($skipLocal && $entry->getProperty('isLocalSystemBook') === true) {
return array_values(array_filter($entries, function (IEntry $entry) use ($skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $restrictEnumerationGroup, $restrictEnumerationPhone, $filter) {
if ($entry->getProperty('UID') === $selfUID) {
return false;
}
// Prevent enumerating local users
if ($disallowEnumeration && $entry->getProperty('isLocalSystemBook')) {
$filterUser = true;
if ($entry->getProperty('isLocalSystemBook')) {
if ($skipLocal) {
return false;
}
$mailAddresses = $entry->getEMailAddresses();
foreach ($mailAddresses as $mailAddress) {
if ($mailAddress === $filter) {
$checkedCommonGroupAlready = false;
// Prevent enumerating local users
if ($disallowEnumeration) {
$filterUser = true;
$mailAddresses = $entry->getEMailAddresses();
foreach ($mailAddresses as $mailAddress) {
if ($mailAddress === $filter) {
$filterUser = false;
break;
}
}
if ($entry->getProperty('UID') && $entry->getProperty('UID') === $filter) {
$filterUser = false;
break;
}
if ($filterUser) {
return false;
}
} elseif ($restrictEnumerationPhone || $restrictEnumerationGroup) {
$canEnumerate = false;
if ($restrictEnumerationPhone) {
$canEnumerate = $this->knownUserService->isKnownToUser($selfUID, $entry->getProperty('UID'));
}
if (!$canEnumerate && $restrictEnumerationGroup) {
$user = $this->userManager->get($entry->getProperty('UID'));
if ($user === null) {
return false;
}
$contactGroups = $this->groupManager->getUserGroupIds($user);
$canEnumerate = !empty(array_intersect($contactGroups, $selfGroups));
$checkedCommonGroupAlready = true;
}
if (!$canEnumerate) {
return false;
}
}
if ($entry->getProperty('UID') && $entry->getProperty('UID') === $filter) {
$filterUser = false;
}
if ($ownGroupsOnly && !$checkedCommonGroupAlready) {
$user = $this->userManager->get($entry->getProperty('UID'));
if ($filterUser) {
return false;
if ($user === null) {
return false;
}
$contactGroups = $this->groupManager->getUserGroupIds($user);
if (empty(array_intersect($contactGroups, $selfGroups))) {
// no groups in common, so shouldn't see the contact
return false;
}
}
}
if ($ownGroupsOnly && $entry->getProperty('isLocalSystemBook') === true) {
$uid = $this->userManager->get($entry->getProperty('UID'));
if ($uid === null) {
return false;
}
$contactGroups = $this->groupManager->getUserGroupIds($uid);
if (count(array_intersect($contactGroups, $selfGroups)) === 0) {
// no groups in common, so shouldn't see the contact
return false;
}
}
return $entry->getProperty('UID') !== $selfUID;
return true;
}));
}

View File

@ -1822,6 +1822,11 @@ class Manager implements IManager {
$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
}
public function limitEnumerationToPhone(): bool {
return $this->allowEnumeration() &&
$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
}
/**
* Copied from \OC_Util::isSharingDisabledForUser
*

View File

@ -384,6 +384,14 @@ interface IManager {
*/
public function limitEnumerationToGroups(): bool;
/**
* Check if user enumeration is limited to the phonebook matches
*
* @return bool
* @since 21.0.1
*/
public function limitEnumerationToPhone(): bool;
/**
* Check if sharing is disabled for the given user
*

View File

@ -26,6 +26,7 @@ namespace Test\Collaboration\Collaborators;
use OC\Collaboration\Collaborators\MailPlugin;
use OC\Collaboration\Collaborators\SearchResult;
use OC\Federation\CloudIdManager;
use OC\KnownUser\KnownUserService;
use OCP\Collaboration\Collaborators\SearchResultType;
use OCP\Contacts\IManager;
use OCP\Federation\ICloudIdManager;
@ -55,6 +56,9 @@ class MailPluginTest extends TestCase {
/** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
protected $groupManager;
/** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
protected $knownUserService;
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
protected $userSession;
@ -64,6 +68,7 @@ class MailPluginTest extends TestCase {
$this->config = $this->createMock(IConfig::class);
$this->contactsManager = $this->createMock(IManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->knownUserService = $this->createMock(KnownUserService::class);
$this->userSession = $this->createMock(IUserSession::class);
$this->cloudIdManager = new CloudIdManager($this->contactsManager);
@ -71,7 +76,14 @@ class MailPluginTest extends TestCase {
}
public function instantiatePlugin() {
$this->plugin = new MailPlugin($this->contactsManager, $this->cloudIdManager, $this->config, $this->groupManager, $this->userSession);
$this->plugin = new MailPlugin(
$this->contactsManager,
$this->cloudIdManager,
$this->config,
$this->groupManager,
$this->knownUserService,
$this->userSession
);
}
/**

View File

@ -25,6 +25,7 @@ namespace Test\Collaboration\Collaborators;
use OC\Collaboration\Collaborators\SearchResult;
use OC\Collaboration\Collaborators\UserPlugin;
use OC\KnownUser\KnownUserService;
use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\IConfig;
use OCP\IGroup;
@ -49,6 +50,9 @@ class UserPluginTest extends TestCase {
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
protected $session;
/** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
protected $knownUserService;
/** @var IUserStatusManager|\PHPUnit\Framework\MockObject\MockObject */
protected $userStatusManager;
@ -78,6 +82,8 @@ class UserPluginTest extends TestCase {
$this->session = $this->createMock(IUserSession::class);
$this->knownUserService = $this->createMock(KnownUserService::class);
$this->userStatusManager = $this->createMock(IUserStatusManager::class);
$this->searchResult = new SearchResult();
@ -93,6 +99,7 @@ class UserPluginTest extends TestCase {
$this->userManager,
$this->groupManager,
$this->session,
$this->knownUserService,
$this->userStatusManager
);
}

View File

@ -26,11 +26,13 @@
namespace Tests\Contacts\ContactsMenu;
use OC\Contacts\ContactsMenu\ContactsStore;
use OC\KnownUser\KnownUserService;
use OCP\Contacts\IManager;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\IUserManager;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
class ContactsStoreTest extends TestCase {
@ -44,6 +46,8 @@ class ContactsStoreTest extends TestCase {
private $groupManager;
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
private $config;
/** @var KnownUserService|MockObject */
private $knownUserService;
protected function setUp(): void {
parent::setUp();
@ -52,7 +56,14 @@ class ContactsStoreTest extends TestCase {
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->config = $this->createMock(IConfig::class);
$this->contactsStore = new ContactsStore($this->contactsManager, $this->config, $this->userManager, $this->groupManager);
$this->knownUserService = $this->createMock(KnownUserService::class);
$this->contactsStore = new ContactsStore(
$this->contactsManager,
$this->config,
$this->userManager,
$this->groupManager,
$this->knownUserService
);
}
public function testGetContactsWithoutFilter() {
@ -171,29 +182,16 @@ class ContactsStoreTest extends TestCase {
}
public function testGetContactsWhenUserIsInExcludeGroups() {
$this->config->expects($this->at(0))->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
->willReturn('yes');
$this->config->expects($this->at(1))
$this->config
->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_restrict_user_enumeration_to_group'), $this->equalTo('no'))
->willReturn('no');
$this->config->expects($this->at(2))
->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups'), $this->equalTo('no'))
->willReturn('yes');
$this->config->expects($this->at(3))
->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_only_share_with_group_members'), $this->equalTo('no'))
->willReturn('yes');
$this->config->expects($this->at(4))
->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups_list'), $this->equalTo(''))
->willReturn('["group1", "group5", "group6"]');
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
['core', 'shareapi_exclude_groups', 'no', 'yes'],
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
['core', 'shareapi_exclude_groups_list', '', '["group1", "group5", "group6"]'],
]);
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class);
@ -228,22 +226,15 @@ class ContactsStoreTest extends TestCase {
}
public function testGetContactsOnlyShareIfInTheSameGroup() {
$this->config->expects($this->at(0))->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
->willReturn('yes');
$this->config->expects($this->at(1)) ->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_restrict_user_enumeration_to_group'), $this->equalTo('no'))
->willReturn('no');
$this->config->expects($this->at(2)) ->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups'), $this->equalTo('no'))
->willReturn('no');
$this->config->expects($this->at(3))
$this->config
->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_only_share_with_group_members'), $this->equalTo('no'))
->willReturn('yes');
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
['core', 'shareapi_exclude_groups', 'no', 'no'],
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
]);
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class);
@ -314,22 +305,15 @@ class ContactsStoreTest extends TestCase {
}
public function testGetContactsOnlyEnumerateIfInTheSameGroup() {
$this->config->expects($this->at(0))->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_allow_share_dialog_user_enumeration'), $this->equalTo('yes'))
->willReturn('yes');
$this->config->expects($this->at(1)) ->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_restrict_user_enumeration_to_group'), $this->equalTo('no'))
->willReturn('yes');
$this->config->expects($this->at(2)) ->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups'), $this->equalTo('no'))
->willReturn('no');
$this->config->expects($this->at(3))
$this->config
->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_only_share_with_group_members'), $this->equalTo('no'))
->willReturn('no');
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
['core', 'shareapi_exclude_groups', 'no', 'no'],
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
]);
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class);
@ -399,6 +383,305 @@ class ContactsStoreTest extends TestCase {
$this->assertEquals('contact', $entries[2]->getProperty('UID'));
}
public function testGetContactsOnlyEnumerateIfPhoneBookMatch() {
$this->config
->method('getAppValue')
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
['core', 'shareapi_exclude_groups', 'no', 'no'],
['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
]);
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class);
$currentUser->expects($this->once())
->method('getUID')
->willReturn('user001');
$this->groupManager->expects($this->at(0))
->method('getUserGroupIds')
->with($this->equalTo($currentUser))
->willReturn(['group1', 'group2', 'group3']);
$this->knownUserService->method('isKnownToUser')
->willReturnMap([
['user001', 'user1', true],
['user001', 'user2', true],
['user001', 'user3', false],
]);
$this->contactsManager->expects($this->once())
->method('search')
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
->willReturn([
[
'UID' => 'user1',
'isLocalSystemBook' => true
],
[
'UID' => 'user2',
'isLocalSystemBook' => true
],
[
'UID' => 'user3',
'isLocalSystemBook' => true
],
[
'UID' => 'contact',
],
]);
$entries = $this->contactsStore->getContacts($currentUser, '');
$this->assertCount(3, $entries);
$this->assertEquals('user1', $entries[0]->getProperty('UID'));
$this->assertEquals('user2', $entries[1]->getProperty('UID'));
$this->assertEquals('contact', $entries[2]->getProperty('UID'));
}
public function testGetContactsOnlyEnumerateIfPhoneBookMatchWithOwnGroupsOnly() {
$this->config
->method('getAppValue')
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
['core', 'shareapi_exclude_groups', 'no', 'no'],
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
]);
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class);
$currentUser->expects($this->once())
->method('getUID')
->willReturn('user001');
$this->groupManager->expects($this->at(0))
->method('getUserGroupIds')
->with($this->equalTo($currentUser))
->willReturn(['group1', 'group2', 'group3']);
$user1 = $this->createMock(IUser::class);
$this->userManager->expects($this->at(0))
->method('get')
->with('user1')
->willReturn($user1);
$this->groupManager->expects($this->at(1))
->method('getUserGroupIds')
->with($this->equalTo($user1))
->willReturn(['group1']);
$user2 = $this->createMock(IUser::class);
$this->userManager->expects($this->at(1))
->method('get')
->with('user2')
->willReturn($user2);
$this->groupManager->expects($this->at(2))
->method('getUserGroupIds')
->with($this->equalTo($user2))
->willReturn(['group2', 'group3']);
$user3 = $this->createMock(IUser::class);
$this->userManager->expects($this->at(2))
->method('get')
->with('user3')
->willReturn($user3);
$this->groupManager->expects($this->at(3))
->method('getUserGroupIds')
->with($this->equalTo($user3))
->willReturn(['group8', 'group9']);
$this->knownUserService->method('isKnownToUser')
->willReturnMap([
['user001', 'user1', true],
['user001', 'user2', true],
['user001', 'user3', true],
]);
$this->contactsManager->expects($this->once())
->method('search')
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
->willReturn([
[
'UID' => 'user1',
'isLocalSystemBook' => true
],
[
'UID' => 'user2',
'isLocalSystemBook' => true
],
[
'UID' => 'user3',
'isLocalSystemBook' => true
],
[
'UID' => 'contact',
],
]);
$entries = $this->contactsStore->getContacts($currentUser, '');
$this->assertCount(3, $entries);
$this->assertEquals('user1', $entries[0]->getProperty('UID'));
$this->assertEquals('user2', $entries[1]->getProperty('UID'));
$this->assertEquals('contact', $entries[2]->getProperty('UID'));
}
public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroup() {
$this->config
->method('getAppValue')
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
['core', 'shareapi_exclude_groups', 'no', 'no'],
['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
]);
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class);
$currentUser->expects($this->once())
->method('getUID')
->willReturn('user001');
$this->groupManager->expects($this->at(0))
->method('getUserGroupIds')
->with($this->equalTo($currentUser))
->willReturn(['group1', 'group2', 'group3']);
$user1 = $this->createMock(IUser::class);
$this->userManager->expects($this->at(0))
->method('get')
->with('user1')
->willReturn($user1);
$this->groupManager->expects($this->at(1))
->method('getUserGroupIds')
->with($this->equalTo($user1))
->willReturn(['group1']);
$this->knownUserService->method('isKnownToUser')
->willReturnMap([
['user001', 'user1', false],
['user001', 'user2', true],
['user001', 'user3', true],
]);
$this->contactsManager->expects($this->once())
->method('search')
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
->willReturn([
[
'UID' => 'user1',
'isLocalSystemBook' => true
],
[
'UID' => 'user2',
'isLocalSystemBook' => true
],
[
'UID' => 'user3',
'isLocalSystemBook' => true
],
[
'UID' => 'contact',
],
]);
$entries = $this->contactsStore->getContacts($currentUser, '');
$this->assertCount(4, $entries);
$this->assertEquals('user1', $entries[0]->getProperty('UID'));
$this->assertEquals('user2', $entries[1]->getProperty('UID'));
$this->assertEquals('user3', $entries[2]->getProperty('UID'));
$this->assertEquals('contact', $entries[3]->getProperty('UID'));
}
public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroupInOwnGroupsOnly() {
$this->config
->method('getAppValue')
->willReturnMap([
['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
['core', 'shareapi_exclude_groups', 'no', 'no'],
['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
]);
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class);
$currentUser->expects($this->once())
->method('getUID')
->willReturn('user001');
$this->groupManager->expects($this->at(0))
->method('getUserGroupIds')
->with($this->equalTo($currentUser))
->willReturn(['group1', 'group2', 'group3']);
$user1 = $this->createMock(IUser::class);
$this->userManager->expects($this->at(0))
->method('get')
->with('user1')
->willReturn($user1);
$this->groupManager->expects($this->at(1))
->method('getUserGroupIds')
->with($this->equalTo($user1))
->willReturn(['group1']);
$user2 = $this->createMock(IUser::class);
$this->userManager->expects($this->at(1))
->method('get')
->with('user2')
->willReturn($user2);
$this->groupManager->expects($this->at(2))
->method('getUserGroupIds')
->with($this->equalTo($user2))
->willReturn(['group2', 'group3']);
$user3 = $this->createMock(IUser::class);
$this->userManager->expects($this->at(2))
->method('get')
->with('user3')
->willReturn($user3);
$this->groupManager->expects($this->at(3))
->method('getUserGroupIds')
->with($this->equalTo($user3))
->willReturn(['group8', 'group9']);
$this->knownUserService->method('isKnownToUser')
->willReturnMap([
['user001', 'user1', false],
['user001', 'user2', true],
['user001', 'user3', true],
]);
$this->contactsManager->expects($this->once())
->method('search')
->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
->willReturn([
[
'UID' => 'user1',
'isLocalSystemBook' => true
],
[
'UID' => 'user2',
'isLocalSystemBook' => true
],
[
'UID' => 'user3',
'isLocalSystemBook' => true
],
[
'UID' => 'contact',
],
]);
$entries = $this->contactsStore->getContacts($currentUser, '');
$this->assertCount(3, $entries);
$this->assertEquals('user1', $entries[0]->getProperty('UID'));
$this->assertEquals('user2', $entries[1]->getProperty('UID'));
$this->assertEquals('contact', $entries[2]->getProperty('UID'));
}
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'))