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 b71268e38b
commit 236aa194e2
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 // Backends
use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\Connector\LegacyDAVACL; use OCA\DAV\Connector\LegacyDAVACL;
use OCA\DAV\CalDAV\CalendarRoot; use OCA\DAV\CalDAV\CalendarRoot;
@ -50,6 +51,7 @@ $principalBackend = new Principal(
\OC::$server->getUserSession(), \OC::$server->getUserSession(),
\OC::$server->getAppManager(), \OC::$server->getAppManager(),
\OC::$server->query(\OCA\DAV\CalDAV\Proxy\ProxyMapper::class), \OC::$server->query(\OCA\DAV\CalDAV\Proxy\ProxyMapper::class),
\OC::$server->get(KnownUserService::class),
\OC::$server->getConfig(), \OC::$server->getConfig(),
'principals/' 'principals/'
); );

View File

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

View File

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

View File

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

View File

@ -36,6 +36,7 @@
namespace OCA\DAV\Connector\Sabre; namespace OCA\DAV\Connector\Sabre;
use OC\KnownUser\KnownUserService;
use OCA\Circles\Exceptions\CircleDoesNotExistException; use OCA\Circles\Exceptions\CircleDoesNotExistException;
use OCA\DAV\CalDAV\Proxy\ProxyMapper; use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\Traits\PrincipalProxyTrait; use OCA\DAV\Traits\PrincipalProxyTrait;
@ -82,27 +83,19 @@ class Principal implements BackendInterface {
/** @var ProxyMapper */ /** @var ProxyMapper */
private $proxyMapper; private $proxyMapper;
/** @var KnownUserService */
private $knownUserService;
/** @var IConfig */ /** @var IConfig */
private $config; 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, public function __construct(IUserManager $userManager,
IGroupManager $groupManager, IGroupManager $groupManager,
IShareManager $shareManager, IShareManager $shareManager,
IUserSession $userSession, IUserSession $userSession,
IAppManager $appManager, IAppManager $appManager,
ProxyMapper $proxyMapper, ProxyMapper $proxyMapper,
KnownUserService $knownUserService,
IConfig $config, IConfig $config,
string $principalPrefix = 'principals/users/') { string $principalPrefix = 'principals/users/') {
$this->userManager = $userManager; $this->userManager = $userManager;
@ -113,6 +106,7 @@ class Principal implements BackendInterface {
$this->principalPrefix = trim($principalPrefix, '/'); $this->principalPrefix = trim($principalPrefix, '/');
$this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/'); $this->hasGroups = $this->hasCircles = ($principalPrefix === 'principals/users/');
$this->proxyMapper = $proxyMapper; $this->proxyMapper = $proxyMapper;
$this->knownUserService = $knownUserService;
$this->config = $config; $this->config = $config;
} }
@ -267,24 +261,24 @@ class Principal implements BackendInterface {
} }
$allowEnumeration = $this->shareManager->allowEnumeration(); $allowEnumeration = $this->shareManager->allowEnumeration();
$limitEnumeration = $this->shareManager->limitEnumerationToGroups(); $limitEnumerationGroup = $this->shareManager->limitEnumerationToGroups();
$limitEnumerationPhone = $this->shareManager->limitEnumerationToPhone();
// 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
$restrictGroups = false; $restrictGroups = false;
$currentUser = $this->userSession->getUser();
if ($this->shareManager->shareWithGroupMembersOnly()) { if ($this->shareManager->shareWithGroupMembersOnly()) {
$user = $this->userSession->getUser(); if (!$currentUser instanceof IUser) {
if (!$user) {
return []; return [];
} }
$restrictGroups = $this->groupManager->getUserGroupIds($user); $restrictGroups = $this->groupManager->getUserGroupIds($currentUser);
} }
$currentUserGroups = []; $currentUserGroups = [];
if ($limitEnumeration) { if ($limitEnumerationGroup) {
$currentUser = $this->userSession->getUser(); if ($currentUser instanceof IUser) {
if ($currentUser) {
$currentUserGroups = $this->groupManager->getUserGroupIds($currentUser); $currentUserGroups = $this->groupManager->getUserGroupIds($currentUser);
} }
} }
@ -302,14 +296,28 @@ class Principal implements BackendInterface {
$users = \array_filter($users, static function (IUser $user) use ($value) { $users = \array_filter($users, static function (IUser $user) use ($value) {
return $user->getEMailAddress() === $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) { if ($limitEnumerationPhone
$users = \array_filter($users, function (IUser $user) use ($currentUserGroups, $value) { && $currentUser instanceof IUser
return !empty(array_intersect( && $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
$this->groupManager->getUserGroupIds($user), // Synced phonebook match
$currentUserGroups return true;
)) || $user->getEMailAddress() === $value; }
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) { $users = \array_filter($users, static function (IUser $user) use ($value) {
return $user->getDisplayName() === $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) { if ($limitEnumerationPhone
$users = \array_filter($users, function (IUser $user) use ($currentUserGroups, $value) { && $currentUser instanceof IUser
return !empty(array_intersect( && $this->knownUserService->isKnownToUser($currentUser->getUID(), $user->getUID())) {
$this->groupManager->getUserGroupIds($user), // Synced phonebook match
$currentUserGroups return true;
)) || $user->getDisplayName() === $value; }
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; namespace OCA\DAV;
use OC\KnownUser\KnownUserService;
use OCA\DAV\AppInfo\PluginManager; use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarRoot; use OCA\DAV\CalDAV\CalendarRoot;
@ -70,6 +71,7 @@ class RootCollection extends SimpleCollection {
\OC::$server->getUserSession(), \OC::$server->getUserSession(),
\OC::$server->getAppManager(), \OC::$server->getAppManager(),
$proxyMapper, $proxyMapper,
\OC::$server->get(KnownUserService::class),
\OC::$server->getConfig() \OC::$server->getConfig()
); );
$groupPrincipalBackend = new GroupPrincipalBackend($groupManager, $userSession, $shareManager, $config); $groupPrincipalBackend = new GroupPrincipalBackend($groupManager, $userSession, $shareManager, $config);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1829,6 +1829,11 @@ class Manager implements IManager {
$this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $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 * Copied from \OC_Util::isSharingDisabledForUser
* *

View File

@ -384,6 +384,14 @@ interface IManager {
*/ */
public function limitEnumerationToGroups(): bool; 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 * 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\MailPlugin;
use OC\Collaboration\Collaborators\SearchResult; use OC\Collaboration\Collaborators\SearchResult;
use OC\Federation\CloudIdManager; use OC\Federation\CloudIdManager;
use OC\KnownUser\KnownUserService;
use OCP\Collaboration\Collaborators\SearchResultType; use OCP\Collaboration\Collaborators\SearchResultType;
use OCP\Contacts\IManager; use OCP\Contacts\IManager;
use OCP\Federation\ICloudIdManager; use OCP\Federation\ICloudIdManager;
@ -55,6 +56,9 @@ class MailPluginTest extends TestCase {
/** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */ /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
protected $groupManager; protected $groupManager;
/** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
protected $knownUserService;
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */ /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
protected $userSession; protected $userSession;
@ -64,6 +68,7 @@ class MailPluginTest extends TestCase {
$this->config = $this->createMock(IConfig::class); $this->config = $this->createMock(IConfig::class);
$this->contactsManager = $this->createMock(IManager::class); $this->contactsManager = $this->createMock(IManager::class);
$this->groupManager = $this->createMock(IGroupManager::class); $this->groupManager = $this->createMock(IGroupManager::class);
$this->knownUserService = $this->createMock(KnownUserService::class);
$this->userSession = $this->createMock(IUserSession::class); $this->userSession = $this->createMock(IUserSession::class);
$this->cloudIdManager = new CloudIdManager($this->contactsManager); $this->cloudIdManager = new CloudIdManager($this->contactsManager);
@ -71,7 +76,14 @@ class MailPluginTest extends TestCase {
} }
public function instantiatePlugin() { 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\SearchResult;
use OC\Collaboration\Collaborators\UserPlugin; use OC\Collaboration\Collaborators\UserPlugin;
use OC\KnownUser\KnownUserService;
use OCP\Collaboration\Collaborators\ISearchResult; use OCP\Collaboration\Collaborators\ISearchResult;
use OCP\IConfig; use OCP\IConfig;
use OCP\IGroup; use OCP\IGroup;
@ -49,6 +50,9 @@ class UserPluginTest extends TestCase {
/** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */ /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
protected $session; protected $session;
/** @var KnownUserService|\PHPUnit\Framework\MockObject\MockObject */
protected $knownUserService;
/** @var IUserStatusManager|\PHPUnit\Framework\MockObject\MockObject */ /** @var IUserStatusManager|\PHPUnit\Framework\MockObject\MockObject */
protected $userStatusManager; protected $userStatusManager;
@ -78,6 +82,8 @@ class UserPluginTest extends TestCase {
$this->session = $this->createMock(IUserSession::class); $this->session = $this->createMock(IUserSession::class);
$this->knownUserService = $this->createMock(KnownUserService::class);
$this->userStatusManager = $this->createMock(IUserStatusManager::class); $this->userStatusManager = $this->createMock(IUserStatusManager::class);
$this->searchResult = new SearchResult(); $this->searchResult = new SearchResult();
@ -93,6 +99,7 @@ class UserPluginTest extends TestCase {
$this->userManager, $this->userManager,
$this->groupManager, $this->groupManager,
$this->session, $this->session,
$this->knownUserService,
$this->userStatusManager $this->userStatusManager
); );
} }

View File

@ -26,11 +26,13 @@
namespace Tests\Contacts\ContactsMenu; namespace Tests\Contacts\ContactsMenu;
use OC\Contacts\ContactsMenu\ContactsStore; use OC\Contacts\ContactsMenu\ContactsStore;
use OC\KnownUser\KnownUserService;
use OCP\Contacts\IManager; use OCP\Contacts\IManager;
use OCP\IConfig; use OCP\IConfig;
use OCP\IGroupManager; use OCP\IGroupManager;
use OCP\IUser; use OCP\IUser;
use OCP\IUserManager; use OCP\IUserManager;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase; use Test\TestCase;
class ContactsStoreTest extends TestCase { class ContactsStoreTest extends TestCase {
@ -44,6 +46,8 @@ class ContactsStoreTest extends TestCase {
private $groupManager; private $groupManager;
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
private $config; private $config;
/** @var KnownUserService|MockObject */
private $knownUserService;
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
@ -52,7 +56,14 @@ class ContactsStoreTest extends TestCase {
$this->userManager = $this->createMock(IUserManager::class); $this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class); $this->groupManager = $this->createMock(IGroupManager::class);
$this->config = $this->createMock(IConfig::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() { public function testGetContactsWithoutFilter() {
@ -171,29 +182,16 @@ class ContactsStoreTest extends TestCase {
} }
public function testGetContactsWhenUserIsInExcludeGroups() { public function testGetContactsWhenUserIsInExcludeGroups() {
$this->config->expects($this->at(0))->method('getAppValue') $this->config
->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') ->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_restrict_user_enumeration_to_group'), $this->equalTo('no')) ->willReturnMap([
->willReturn('no'); ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
$this->config->expects($this->at(2)) ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
->method('getAppValue') ['core', 'shareapi_exclude_groups', 'no', 'yes'],
->with($this->equalTo('core'), $this->equalTo('shareapi_exclude_groups'), $this->equalTo('no')) ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
->willReturn('yes'); ['core', 'shareapi_exclude_groups_list', '', '["group1", "group5", "group6"]'],
]);
$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"]');
/** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */ /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class); $currentUser = $this->createMock(IUser::class);
@ -228,22 +226,15 @@ class ContactsStoreTest extends TestCase {
} }
public function testGetContactsOnlyShareIfInTheSameGroup() { public function testGetContactsOnlyShareIfInTheSameGroup() {
$this->config->expects($this->at(0))->method('getAppValue') $this->config
->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))
->method('getAppValue') ->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_only_share_with_group_members'), $this->equalTo('no')) ->willReturnMap([
->willReturn('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_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 */ /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class); $currentUser = $this->createMock(IUser::class);
@ -314,22 +305,15 @@ class ContactsStoreTest extends TestCase {
} }
public function testGetContactsOnlyEnumerateIfInTheSameGroup() { public function testGetContactsOnlyEnumerateIfInTheSameGroup() {
$this->config->expects($this->at(0))->method('getAppValue') $this->config
->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))
->method('getAppValue') ->method('getAppValue')
->with($this->equalTo('core'), $this->equalTo('shareapi_only_share_with_group_members'), $this->equalTo('no')) ->willReturnMap([
->willReturn('no'); ['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 */ /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $currentUser */
$currentUser = $this->createMock(IUser::class); $currentUser = $this->createMock(IUser::class);
@ -399,6 +383,305 @@ class ContactsStoreTest extends TestCase {
$this->assertEquals('contact', $entries[2]->getProperty('UID')); $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() { public function testGetContactsWithFilter() {
$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'))