diff --git a/apps/files_sharing/lib/Controller/ShareesAPIController.php b/apps/files_sharing/lib/Controller/ShareesAPIController.php index 94a4854dbde8..dcecf2c8e081 100644 --- a/apps/files_sharing/lib/Controller/ShareesAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareesAPIController.php @@ -6,6 +6,7 @@ * @author Joas Schilling * @author Roeland Jago Douma * @author Thomas Müller + * @author Arthur Schiwon * * @license AGPL-3.0 * @@ -27,52 +28,23 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCSController; -use OCP\Contacts\IManager; -use OCP\Federation\ICloudIdManager; -use OCP\Http\Client\IClientService; -use OCP\IGroup; -use OCP\IGroupManager; -use OCP\ILogger; +use OCP\Collaboration\Collaborators\ISearch; use OCP\IRequest; -use OCP\IUser; -use OCP\IUserManager; use OCP\IConfig; -use OCP\IUserSession; use OCP\IURLGenerator; use OCP\Share; +use OCP\Share\IManager; class ShareesAPIController extends OCSController { - - /** @var IGroupManager */ - protected $groupManager; - - /** @var IUserManager */ - protected $userManager; - - /** @var IManager */ - protected $contactsManager; - /** @var IConfig */ protected $config; - /** @var IUserSession */ - protected $userSession; - /** @var IURLGenerator */ protected $urlGenerator; - /** @var ILogger */ - protected $logger; - - /** @var \OCP\Share\IManager */ + /** @var IManager */ protected $shareManager; - /** @var IClientService */ - protected $clientService; - - /** @var ICloudIdManager */ - protected $cloudIdManager; - /** @var bool */ protected $shareWithGroupOnly = false; @@ -103,326 +75,31 @@ class ShareesAPIController extends OCSController { ]; protected $reachedEndFor = []; + /** @var ISearch */ + private $collaboratorSearch; /** * @param string $appName * @param IRequest $request - * @param IGroupManager $groupManager - * @param IUserManager $userManager - * @param IManager $contactsManager * @param IConfig $config - * @param IUserSession $userSession * @param IURLGenerator $urlGenerator - * @param ILogger $logger - * @param \OCP\Share\IManager $shareManager - * @param IClientService $clientService - * @param ICloudIdManager $cloudIdManager + * @param IManager $shareManager + * @param ISearch $collaboratorSearch */ - public function __construct($appName, - IRequest $request, - IGroupManager $groupManager, - IUserManager $userManager, - IManager $contactsManager, - IConfig $config, - IUserSession $userSession, - IURLGenerator $urlGenerator, - ILogger $logger, - \OCP\Share\IManager $shareManager, - IClientService $clientService, - ICloudIdManager $cloudIdManager + public function __construct( + $appName, + IRequest $request, + IConfig $config, + IURLGenerator $urlGenerator, + IManager $shareManager, + ISearch $collaboratorSearch ) { parent::__construct($appName, $request); - $this->groupManager = $groupManager; - $this->userManager = $userManager; - $this->contactsManager = $contactsManager; $this->config = $config; - $this->userSession = $userSession; $this->urlGenerator = $urlGenerator; - $this->logger = $logger; $this->shareManager = $shareManager; - $this->clientService = $clientService; - $this->cloudIdManager = $cloudIdManager; - } - - /** - * @param string $search - */ - protected function getUsers($search) { - $this->result['users'] = $this->result['exact']['users'] = $users = []; - - $userGroups = []; - if ($this->shareWithGroupOnly) { - // Search in all the groups this user is part of - $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); - foreach ($userGroups as $userGroup) { - $usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $this->limit, $this->offset); - foreach ($usersTmp as $uid => $userDisplayName) { - $users[$uid] = $userDisplayName; - } - } - } else { - // Search in all users - $usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset); - - foreach ($usersTmp as $user) { - $users[$user->getUID()] = $user->getDisplayName(); - } - } - - if (!$this->shareeEnumeration || sizeof($users) < $this->limit) { - $this->reachedEndFor[] = 'users'; - } - - $foundUserById = false; - $lowerSearch = strtolower($search); - foreach ($users as $uid => $userDisplayName) { - if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) { - if (strtolower($uid) === $lowerSearch) { - $foundUserById = true; - } - $this->result['exact']['users'][] = [ - 'label' => $userDisplayName, - 'value' => [ - 'shareType' => Share::SHARE_TYPE_USER, - 'shareWith' => $uid, - ], - ]; - } else { - $this->result['users'][] = [ - 'label' => $userDisplayName, - 'value' => [ - 'shareType' => Share::SHARE_TYPE_USER, - 'shareWith' => $uid, - ], - ]; - } - } - - if ($this->offset === 0 && !$foundUserById) { - // On page one we try if the search result has a direct hit on the - // user id and if so, we add that to the exact match list - $user = $this->userManager->get($search); - if ($user instanceof IUser) { - $addUser = true; - - if ($this->shareWithGroupOnly) { - // Only add, if we have a common group - $commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user)); - $addUser = !empty($commonGroups); - } - - if ($addUser) { - array_push($this->result['exact']['users'], [ - 'label' => $user->getDisplayName(), - 'value' => [ - 'shareType' => Share::SHARE_TYPE_USER, - 'shareWith' => $user->getUID(), - ], - ]); - } - } - } - - if (!$this->shareeEnumeration) { - $this->result['users'] = []; - } - } - - /** - * @param string $search - */ - protected function getGroups($search) { - $this->result['groups'] = $this->result['exact']['groups'] = []; - - $groups = $this->groupManager->search($search, $this->limit, $this->offset); - $groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups); - - if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) { - $this->reachedEndFor[] = 'groups'; - } - - $userGroups = []; - if (!empty($groups) && $this->shareWithGroupOnly) { - // Intersect all the groups that match with the groups this user is a member of - $userGroups = $this->groupManager->getUserGroups($this->userSession->getUser()); - $userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups); - $groupIds = array_intersect($groupIds, $userGroups); - } - - $lowerSearch = strtolower($search); - foreach ($groups as $group) { - // FIXME: use a more efficient approach - $gid = $group->getGID(); - if (!in_array($gid, $groupIds)) { - continue; - } - if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) { - $this->result['exact']['groups'][] = [ - 'label' => $group->getDisplayName(), - 'value' => [ - 'shareType' => Share::SHARE_TYPE_GROUP, - 'shareWith' => $gid, - ], - ]; - } else { - $this->result['groups'][] = [ - 'label' => $group->getDisplayName(), - 'value' => [ - 'shareType' => Share::SHARE_TYPE_GROUP, - 'shareWith' => $gid, - ], - ]; - } - } - - if ($this->offset === 0 && empty($this->result['exact']['groups'])) { - // On page one we try if the search result has a direct hit on the - // user id and if so, we add that to the exact match list - $group = $this->groupManager->get($search); - if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) { - array_push($this->result['exact']['groups'], [ - 'label' => $group->getDisplayName(), - 'value' => [ - 'shareType' => Share::SHARE_TYPE_GROUP, - 'shareWith' => $group->getGID(), - ], - ]); - } - } - - if (!$this->shareeEnumeration) { - $this->result['groups'] = []; - } - } - - - /** - * @param string $search - * @suppress PhanUndeclaredClassMethod - */ - protected function getCircles($search) { - $this->result['circles'] = $this->result['exact']['circles'] = []; - - $result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset); - if (array_key_exists('circles', $result['exact'])) { - $this->result['exact']['circles'] = $result['exact']['circles']; - } - if (array_key_exists('circles', $result)) { - $this->result['circles'] = $result['circles']; - } - } - - - /** - * @param string $search - * @return array - */ - protected function getRemote($search) { - $result = ['results' => [], 'exact' => []]; - - // Search in contacts - //@todo Pagination missing - $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']); - $result['exactIdMatch'] = false; - foreach ($addressBookContacts as $contact) { - if (isset($contact['isLocalSystemBook'])) { - continue; - } - if (isset($contact['CLOUD'])) { - $cloudIds = $contact['CLOUD']; - if (!is_array($cloudIds)) { - $cloudIds = [$cloudIds]; - } - $lowerSearch = strtolower($search); - foreach ($cloudIds as $cloudId) { - try { - list(, $serverUrl) = $this->splitUserRemote($cloudId); - } catch (\InvalidArgumentException $e) { - continue; - } - - if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) { - if (strtolower($cloudId) === $lowerSearch) { - $result['exactIdMatch'] = true; - } - $result['exact'][] = [ - 'label' => $contact['FN'] . " ($cloudId)", - 'value' => [ - 'shareType' => Share::SHARE_TYPE_REMOTE, - 'shareWith' => $cloudId, - 'server' => $serverUrl, - ], - ]; - } else { - $result['results'][] = [ - 'label' => $contact['FN'] . " ($cloudId)", - 'value' => [ - 'shareType' => Share::SHARE_TYPE_REMOTE, - 'shareWith' => $cloudId, - 'server' => $serverUrl, - ], - ]; - } - } - } - } - - if (!$this->shareeEnumeration) { - $result['results'] = []; - } - - if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) { - $result['exact'][] = [ - 'label' => $search, - 'value' => [ - 'shareType' => Share::SHARE_TYPE_REMOTE, - 'shareWith' => $search, - ], - ]; - } - - $this->reachedEndFor[] = 'remotes'; - - return $result; - } - - /** - * split user and remote from federated cloud id - * - * @param string $address federated share address - * @return array [user, remoteURL] - * @throws \InvalidArgumentException - */ - public function splitUserRemote($address) { - try { - $cloudId = $this->cloudIdManager->resolveCloudId($address); - return [$cloudId->getUser(), $cloudId->getRemote()]; - } catch (\InvalidArgumentException $e) { - throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e); - } - } - - /** - * Strips away a potential file names and trailing slashes: - * - http://localhost - * - http://localhost/ - * - http://localhost/index.php - * - http://localhost/index.php/s/{shareToken} - * - * all return: http://localhost - * - * @param string $remote - * @return string - */ - protected function fixRemoteURL($remote) { - $remote = str_replace('\\', '/', $remote); - if ($fileNamePosition = strpos($remote, '/index.php')) { - $remote = substr($remote, 0, $fileNamePosition); - } - $remote = rtrim($remote, '/'); - - return $remote; + $this->collaboratorSearch = $collaboratorSearch; } /** @@ -461,7 +138,9 @@ public function search($search = '', $itemType = null, $page = 1, $perPage = 200 Share::SHARE_TYPE_USER, ]; - if ($itemType === 'file' || $itemType === 'folder') { + if ($itemType === null) { + throw new OCSBadRequestException('Missing itemType'); + } elseif ($itemType === 'file' || $itemType === 'folder') { if ($this->shareManager->allowGroupSharing()) { $shareTypes[] = Share::SHARE_TYPE_GROUP; } @@ -478,6 +157,7 @@ public function search($search = '', $itemType = null, $page = 1, $perPage = 200 $shareTypes[] = Share::SHARE_TYPE_EMAIL; } + // FIXME: DI if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) { $shareTypes[] = Share::SHARE_TYPE_CIRCLE; } @@ -495,94 +175,16 @@ public function search($search = '', $itemType = null, $page = 1, $perPage = 200 $this->limit = (int) $perPage; $this->offset = $perPage * ($page - 1); - return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup); - } - - /** - * Method to get out the static call for better testing - * - * @param string $itemType - * @return bool - */ - protected function isRemoteSharingAllowed($itemType) { - try { - $backend = \OC\Share\Share::getBackend($itemType); - return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE); - } catch (\Exception $e) { - return false; - } - } - - /** - * Testable search function that does not need globals - * - * @param string $search - * @param string $itemType - * @param array $shareTypes - * @param int $page - * @param int $perPage - * @param bool $lookup - * @return DataResponse - * @throws OCSBadRequestException - */ - protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) { - // Verify arguments - if ($itemType === null) { - throw new OCSBadRequestException('Missing itemType'); - } - - // Get users - if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) { - $this->getUsers($search); - } - - // Get groups - if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) { - $this->getGroups($search); - } - - // Get circles - if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) { - $this->getCircles($search); - } - - - // Get remote - $remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; - if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) { - $remoteResults = $this->getRemote($search); - } - - // Get emails - $mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; - if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) { - $mailResults = $this->getEmail($search); - } - - // Get from lookup server - if ($lookup) { - $this->getLookup($search); - } + list($result, $hasMoreResults) = $this->collaboratorSearch->search($search, $shareTypes, $lookup, $this->limit, $this->offset); - // if we have a exact match, either for the federated cloud id or for the - // email address we only return the exact match. It is highly unlikely - // that the exact same email address and federated cloud id exists - if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) { - $this->result['emails'] = $mailResults['results']; - $this->result['exact']['emails'] = $mailResults['exact']; - } else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) { - $this->result['remotes'] = $remoteResults['results']; - $this->result['exact']['remotes'] = $remoteResults['exact']; - } else { - $this->result['remotes'] = $remoteResults['results']; - $this->result['exact']['remotes'] = $remoteResults['exact']; - $this->result['emails'] = $mailResults['results']; - $this->result['exact']['emails'] = $mailResults['exact']; + // extra treatment for 'exact' subarray, with a single merge expected keys might be lost + if(isset($result['exact'])) { + $result['exact'] = array_merge($this->result['exact'], $result['exact']); } - + $this->result = array_merge($this->result, $result); $response = new DataResponse($this->result); - if (sizeof($this->reachedEndFor) < 3) { + if ($hasMoreResults) { $response->addHeader('Link', $this->getPaginationLink($page, [ 'search' => $search, 'itemType' => $itemType, @@ -595,166 +197,22 @@ protected function searchSharees($search, $itemType, array $shareTypes, $page, $ } /** - * @param string $search - * @return array - */ - protected function getEmail($search) { - $result = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; - - // Search in contacts - //@todo Pagination missing - $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']); - $lowerSearch = strtolower($search); - foreach ($addressBookContacts as $contact) { - if (isset($contact['EMAIL'])) { - $emailAddresses = $contact['EMAIL']; - if (!is_array($emailAddresses)) { - $emailAddresses = [$emailAddresses]; - } - foreach ($emailAddresses as $emailAddress) { - $exactEmailMatch = strtolower($emailAddress) === $lowerSearch; - - if (isset($contact['isLocalSystemBook'])) { - if ($exactEmailMatch) { - try { - $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); - } catch (\InvalidArgumentException $e) { - continue; - } - - if (!$this->hasUserInResult($cloud->getUser())) { - $this->result['exact']['users'][] = [ - 'label' => $contact['FN'] . " ($emailAddress)", - 'value' => [ - 'shareType' => Share::SHARE_TYPE_USER, - 'shareWith' => $cloud->getUser(), - ], - ]; - } - return ['results' => [], 'exact' => [], 'exactIdMatch' => true]; - } - - if ($this->shareeEnumeration) { - try { - $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); - } catch (\InvalidArgumentException $e) { - continue; - } - - if (!$this->hasUserInResult($cloud->getUser())) { - $this->result['users'][] = [ - 'label' => $contact['FN'] . " ($emailAddress)", - 'value' => [ - 'shareType' => Share::SHARE_TYPE_USER, - 'shareWith' => $cloud->getUser(), - ], - ]; - } - } - continue; - } - - if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) { - if ($exactEmailMatch) { - $result['exactIdMatch'] = true; - } - $result['exact'][] = [ - 'label' => $contact['FN'] . " ($emailAddress)", - 'value' => [ - 'shareType' => Share::SHARE_TYPE_EMAIL, - 'shareWith' => $emailAddress, - ], - ]; - } else { - $result['results'][] = [ - 'label' => $contact['FN'] . " ($emailAddress)", - 'value' => [ - 'shareType' => Share::SHARE_TYPE_EMAIL, - 'shareWith' => $emailAddress, - ], - ]; - } - } - } - } - - if (!$this->shareeEnumeration) { - $result['results'] = []; - } - - if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) { - $result['exact'][] = [ - 'label' => $search, - 'value' => [ - 'shareType' => Share::SHARE_TYPE_EMAIL, - 'shareWith' => $search, - ], - ]; - } - - $this->reachedEndFor[] = 'emails'; - - return $result; - } - - protected function getLookup($search) { - $isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); - $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com'); - $lookupServerUrl = rtrim($lookupServerUrl, '/'); - $result = []; - - if($isEnabled === 'yes') { - try { - $client = $this->clientService->newClient(); - $response = $client->get( - $lookupServerUrl . '/users?search=' . urlencode($search), - [ - 'timeout' => 10, - 'connect_timeout' => 3, - ] - ); - - $body = json_decode($response->getBody(), true); - - $result = []; - foreach ($body as $lookup) { - $result[] = [ - 'label' => $lookup['federationId'], - 'value' => [ - 'shareType' => Share::SHARE_TYPE_REMOTE, - 'shareWith' => $lookup['federationId'], - ], - 'extra' => $lookup, - ]; - } - } catch (\Exception $e) {} - } - - $this->result['lookup'] = $result; - } - - /** - * Check if a given user is already part of the result + * Method to get out the static call for better testing * - * @param string $userId + * @param string $itemType * @return bool */ - protected function hasUserInResult($userId) { - foreach ($this->result['exact']['users'] as $result) { - if ($result['value']['shareWith'] === $userId) { - return true; - } - } - - foreach ($this->result['users'] as $result) { - if ($result['value']['shareWith'] === $userId) { - return true; - } + protected function isRemoteSharingAllowed($itemType) { + try { + // FIXME: static foo makes unit testing unnecessarily difficult + $backend = \OC\Share\Share::getBackend($itemType); + return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE); + } catch (\Exception $e) { + return false; } - - return false; } + /** * Generates a bunch of pagination links for the current page * diff --git a/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php index e027d0751cb7..2b919f4849db 100644 --- a/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php @@ -25,14 +25,16 @@ namespace OCA\Files_Sharing\Tests\Controller; -use OC\Federation\CloudIdManager; use OCA\Files_Sharing\Controller\ShareesAPIController; use OCA\Files_Sharing\Tests\TestCase; use OCP\AppFramework\Http; use OCP\AppFramework\OCS\OCSBadRequestException; -use OCP\Federation\ICloudIdManager; -use OCP\Http\Client\IClientService; +use OCP\Collaboration\Collaborators\ISearch; +use OCP\IConfig; +use OCP\IRequest; +use OCP\IURLGenerator; use OCP\Share; +use OCP\Share\IManager; /** * Class ShareesTest @@ -45,1251 +47,39 @@ class ShareesAPIControllerTest extends TestCase { /** @var ShareesAPIController */ protected $sharees; - /** @var \OCP\IUserManager|\PHPUnit_Framework_MockObject_MockObject */ - protected $userManager; - - /** @var \OCP\IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ - protected $groupManager; - - /** @var \OCP\Contacts\IManager|\PHPUnit_Framework_MockObject_MockObject */ - protected $contactsManager; - - /** @var \OCP\IUserSession|\PHPUnit_Framework_MockObject_MockObject */ - protected $session; - - /** @var \OCP\IRequest|\PHPUnit_Framework_MockObject_MockObject */ + /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ protected $request; - /** @var \OCP\Share\IManager|\PHPUnit_Framework_MockObject_MockObject */ + /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */ protected $shareManager; - /** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */ - private $clientService; - - /** @var ICloudIdManager */ - private $cloudIdManager; + /** @var ISearch|\PHPUnit_Framework_MockObject_MockObject */ + protected $collaboratorSearch; protected function setUp() { parent::setUp(); - $this->userManager = $this->getMockBuilder('OCP\IUserManager') - ->disableOriginalConstructor() - ->getMock(); + $this->request = $this->createMock(IRequest::class); + $this->shareManager = $this->createMock(IManager::class); - $this->groupManager = $this->getMockBuilder('OCP\IGroupManager') - ->disableOriginalConstructor() - ->getMock(); + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject $configMock */ + $configMock = $this->createMock(IConfig::class); - $this->contactsManager = $this->getMockBuilder('OCP\Contacts\IManager') - ->disableOriginalConstructor() - ->getMock(); + /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject $urlGeneratorMock */ + $urlGeneratorMock = $this->createMock(IURLGenerator::class); - $this->session = $this->getMockBuilder('OCP\IUserSession') - ->disableOriginalConstructor() - ->getMock(); - - $this->request = $this->getMockBuilder('OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - - $this->shareManager = $this->getMockBuilder('OCP\Share\IManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->clientService = $this->createMock(IClientService::class); - - $this->cloudIdManager = new CloudIdManager(); + $this->collaboratorSearch = $this->createMock(ISearch::class); $this->sharees = new ShareesAPIController( 'files_sharing', $this->request, - $this->groupManager, - $this->userManager, - $this->contactsManager, - $this->getMockBuilder('OCP\IConfig')->disableOriginalConstructor()->getMock(), - $this->session, - $this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(), - $this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(), + $configMock, + $urlGeneratorMock, $this->shareManager, - $this->clientService, - $this->cloudIdManager + $this->collaboratorSearch ); } - /** - * @param string $uid - * @param string $displayName - * @return \OCP\IUser|\PHPUnit_Framework_MockObject_MockObject - */ - protected function getUserMock($uid, $displayName) { - $user = $this->getMockBuilder('OCP\IUser') - ->disableOriginalConstructor() - ->getMock(); - - $user->expects($this->any()) - ->method('getUID') - ->willReturn($uid); - - $user->expects($this->any()) - ->method('getDisplayName') - ->willReturn($displayName); - - return $user; - } - - /** - * @param string $gid - * @return \OCP\IGroup|\PHPUnit_Framework_MockObject_MockObject - */ - protected function getGroupMock($gid, $displayName = null) { - $group = $this->getMockBuilder('OCP\IGroup') - ->disableOriginalConstructor() - ->getMock(); - - $group->expects($this->any()) - ->method('getGID') - ->willReturn($gid); - - if (is_null($displayName)) { - // note: this is how the Group class behaves - $displayName = $gid; - } - - $group->expects($this->any()) - ->method('getDisplayName') - ->willReturn($displayName); - - return $group; - } - - public function dataGetUsers() { - return [ - ['test', false, true, [], [], [], [], true, false], - ['test', false, false, [], [], [], [], true, false], - ['test', true, true, [], [], [], [], true, false], - ['test', true, false, [], [], [], [], true, false], - [ - 'test', false, true, [], [], - [ - ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], - ], [], true, $this->getUserMock('test', 'Test') - ], - [ - 'test', false, false, [], [], - [ - ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], - ], [], true, $this->getUserMock('test', 'Test') - ], - [ - 'test', true, true, [], [], - [], [], true, $this->getUserMock('test', 'Test') - ], - [ - 'test', true, false, [], [], - [], [], true, $this->getUserMock('test', 'Test') - ], - [ - 'test', true, true, ['test-group'], [['test-group', 'test', 2, 0, []]], - [ - ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], - ], [], true, $this->getUserMock('test', 'Test') - ], - [ - 'test', true, false, ['test-group'], [['test-group', 'test', 2, 0, []]], - [ - ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], - ], [], true, $this->getUserMock('test', 'Test') - ], - [ - 'test', - false, - true, - [], - [ - $this->getUserMock('test1', 'Test One'), - ], - [], - [ - ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ], - true, - false, - ], - [ - 'test', - false, - false, - [], - [ - $this->getUserMock('test1', 'Test One'), - ], - [], - [], - true, - false, - ], - [ - 'test', - false, - true, - [], - [ - $this->getUserMock('test1', 'Test One'), - $this->getUserMock('test2', 'Test Two'), - ], - [], - [ - ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ['label' => 'Test Two', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], - ], - false, - false, - ], - [ - 'test', - false, - false, - [], - [ - $this->getUserMock('test1', 'Test One'), - $this->getUserMock('test2', 'Test Two'), - ], - [], - [], - true, - false, - ], - [ - 'test', - false, - true, - [], - [ - $this->getUserMock('test0', 'Test'), - $this->getUserMock('test1', 'Test One'), - $this->getUserMock('test2', 'Test Two'), - ], - [ - ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test0']], - ], - [ - ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ['label' => 'Test Two', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], - ], - false, - false, - ], - [ - 'test', - false, - false, - [], - [ - $this->getUserMock('test0', 'Test'), - $this->getUserMock('test1', 'Test One'), - $this->getUserMock('test2', 'Test Two'), - ], - [ - ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test0']], - ], - [], - true, - false, - ], - [ - 'test', - true, - true, - ['abc', 'xyz'], - [ - ['abc', 'test', 2, 0, ['test1' => 'Test One']], - ['xyz', 'test', 2, 0, []], - ], - [], - [ - ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ], - true, - false, - ], - [ - 'test', - true, - false, - ['abc', 'xyz'], - [ - ['abc', 'test', 2, 0, ['test1' => 'Test One']], - ['xyz', 'test', 2, 0, []], - ], - [], - [], - true, - false, - ], - [ - 'test', - true, - true, - ['abc', 'xyz'], - [ - ['abc', 'test', 2, 0, [ - 'test1' => 'Test One', - 'test2' => 'Test Two', - ]], - ['xyz', 'test', 2, 0, [ - 'test1' => 'Test One', - 'test2' => 'Test Two', - ]], - ], - [], - [ - ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ['label' => 'Test Two', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], - ], - false, - false, - ], - [ - 'test', - true, - false, - ['abc', 'xyz'], - [ - ['abc', 'test', 2, 0, [ - 'test1' => 'Test One', - 'test2' => 'Test Two', - ]], - ['xyz', 'test', 2, 0, [ - 'test1' => 'Test One', - 'test2' => 'Test Two', - ]], - ], - [], - [], - true, - false, - ], - [ - 'test', - true, - true, - ['abc', 'xyz'], - [ - ['abc', 'test', 2, 0, [ - 'test' => 'Test One', - ]], - ['xyz', 'test', 2, 0, [ - 'test2' => 'Test Two', - ]], - ], - [ - ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], - ], - [ - ['label' => 'Test Two', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], - ], - false, - false, - ], - [ - 'test', - true, - false, - ['abc', 'xyz'], - [ - ['abc', 'test', 2, 0, [ - 'test' => 'Test One', - ]], - ['xyz', 'test', 2, 0, [ - 'test2' => 'Test Two', - ]], - ], - [ - ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], - ], - [], - true, - false, - ], - ]; - } - - /** - * @dataProvider dataGetUsers - * - * @param string $searchTerm - * @param bool $shareWithGroupOnly - * @param bool $shareeEnumeration - * @param array $groupResponse - * @param array $userResponse - * @param array $exactExpected - * @param array $expected - * @param bool $reachedEnd - * @param mixed $singleUser - */ - public function testGetUsers($searchTerm, $shareWithGroupOnly, $shareeEnumeration, $groupResponse, $userResponse, $exactExpected, $expected, $reachedEnd, $singleUser) { - $this->invokePrivate($this->sharees, 'limit', [2]); - $this->invokePrivate($this->sharees, 'offset', [0]); - $this->invokePrivate($this->sharees, 'shareWithGroupOnly', [$shareWithGroupOnly]); - $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); - - $user = $this->getUserMock('admin', 'Administrator'); - $this->session->expects($this->any()) - ->method('getUser') - ->willReturn($user); - - if (!$shareWithGroupOnly) { - $this->userManager->expects($this->once()) - ->method('searchDisplayName') - ->with($searchTerm, $this->invokePrivate($this->sharees, 'limit'), $this->invokePrivate($this->sharees, 'offset')) - ->willReturn($userResponse); - } else { - if ($singleUser !== false) { - $this->groupManager->expects($this->exactly(2)) - ->method('getUserGroupIds') - ->withConsecutive( - $user, - $singleUser - ) - ->willReturn($groupResponse); - } else { - $this->groupManager->expects($this->once()) - ->method('getUserGroupIds') - ->with($user) - ->willReturn($groupResponse); - } - - $this->groupManager->expects($this->exactly(sizeof($groupResponse))) - ->method('displayNamesInGroup') - ->with($this->anything(), $searchTerm, $this->invokePrivate($this->sharees, 'limit'), $this->invokePrivate($this->sharees, 'offset')) - ->willReturnMap($userResponse); - } - - if ($singleUser !== false) { - $this->userManager->expects($this->once()) - ->method('get') - ->with($searchTerm) - ->willReturn($singleUser); - } - - $this->invokePrivate($this->sharees, 'getUsers', [$searchTerm]); - $result = $this->invokePrivate($this->sharees, 'result'); - - $this->assertEquals($exactExpected, $result['exact']['users']); - $this->assertEquals($expected, $result['users']); - $this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor')); - } - - public function dataGetGroups() { - return [ - ['test', false, true, [], [], [], [], true, false], - ['test', false, false, [], [], [], [], true, false], - // group without display name - [ - 'test', false, true, - [$this->getGroupMock('test1')], - [], - [], - [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], - true, - false, - ], - // group with display name, search by id - [ - 'test', false, true, - [$this->getGroupMock('test1', 'Test One')], - [], - [], - [['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], - true, - false, - ], - // group with display name, search by display name - [ - 'one', false, true, - [$this->getGroupMock('test1', 'Test One')], - [], - [], - [['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], - true, - false, - ], - // group with display name, search by display name, exact expected - [ - 'Test One', false, true, - [$this->getGroupMock('test1', 'Test One')], - [], - [['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], - [], - true, - false, - ], - [ - 'test', false, false, - [$this->getGroupMock('test1')], - [], - [], - [], - true, - false, - ], - [ - 'test', false, true, - [ - $this->getGroupMock('test'), - $this->getGroupMock('test1'), - ], - [], - [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], - [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], - false, - false, - ], - [ - 'test', false, false, - [ - $this->getGroupMock('test'), - $this->getGroupMock('test1'), - ], - [], - [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], - [], - true, - false, - ], - [ - 'test', false, true, - [ - $this->getGroupMock('test0'), - $this->getGroupMock('test1'), - ], - [], - [], - [ - ['label' => 'test0', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test0']], - ['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']], - ], - false, - null, - ], - [ - 'test', false, false, - [ - $this->getGroupMock('test0'), - $this->getGroupMock('test1'), - ], - [], - [], - [], - true, - null, - ], - [ - 'test', false, true, - [ - $this->getGroupMock('test0'), - $this->getGroupMock('test1'), - ], - [], - [ - ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], - ], - [ - ['label' => 'test0', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test0']], - ['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']], - ], - false, - $this->getGroupMock('test'), - ], - [ - 'test', false, false, - [ - $this->getGroupMock('test0'), - $this->getGroupMock('test1'), - ], - [], - [ - ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], - ], - [], - true, - $this->getGroupMock('test'), - ], - ['test', true, true, [], [], [], [], true, false], - ['test', true, false, [], [], [], [], true, false], - [ - 'test', true, true, - [ - $this->getGroupMock('test1'), - $this->getGroupMock('test2'), - ], - [$this->getGroupMock('test1')], - [], - [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], - false, - false, - ], - [ - 'test', true, false, - [ - $this->getGroupMock('test1'), - $this->getGroupMock('test2'), - ], - [$this->getGroupMock('test1')], - [], - [], - true, - false, - ], - [ - 'test', true, true, - [ - $this->getGroupMock('test'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test')], - [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], - [], - false, - false, - ], - [ - 'test', true, false, - [ - $this->getGroupMock('test'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test')], - [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], - [], - true, - false, - ], - [ - 'test', true, true, - [ - $this->getGroupMock('test'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test1')], - [], - [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], - false, - false, - ], - [ - 'test', true, false, - [ - $this->getGroupMock('test'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test1')], - [], - [], - true, - false, - ], - [ - 'test', true, true, - [ - $this->getGroupMock('test'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], - [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], - [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], - false, - false, - ], - [ - 'test', true, false, - [ - $this->getGroupMock('test'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], - [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], - [], - true, - false, - ], - [ - 'test', true, true, - [ - $this->getGroupMock('test0'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], - [], - [ - ['label' => 'test0', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test0']], - ['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']], - ], - false, - null, - ], - [ - 'test', true, false, - [ - $this->getGroupMock('test0'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], - [], - [], - true, - null, - ], - [ - 'test', true, true, - [ - $this->getGroupMock('test0'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], - [ - ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], - ], - [ - ['label' => 'test0', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test0']], - ['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']], - ], - false, - $this->getGroupMock('test'), - ], - [ - 'test', true, false, - [ - $this->getGroupMock('test0'), - $this->getGroupMock('test1'), - ], - [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], - [ - ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], - ], - [], - true, - $this->getGroupMock('test'), - ], - ]; - } - - /** - * @dataProvider dataGetGroups - * - * @param string $searchTerm - * @param bool $shareWithGroupOnly - * @param bool $shareeEnumeration - * @param array $groupResponse - * @param array $userGroupsResponse - * @param array $exactExpected - * @param array $expected - * @param bool $reachedEnd - * @param mixed $singleGroup - */ - public function testGetGroups($searchTerm, $shareWithGroupOnly, $shareeEnumeration, $groupResponse, $userGroupsResponse, $exactExpected, $expected, $reachedEnd, $singleGroup) { - $this->invokePrivate($this->sharees, 'limit', [2]); - $this->invokePrivate($this->sharees, 'offset', [0]); - $this->invokePrivate($this->sharees, 'shareWithGroupOnly', [$shareWithGroupOnly]); - $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); - - $this->groupManager->expects($this->once()) - ->method('search') - ->with($searchTerm, $this->invokePrivate($this->sharees, 'limit'), $this->invokePrivate($this->sharees, 'offset')) - ->willReturn($groupResponse); - - if ($singleGroup !== false) { - $this->groupManager->expects($this->once()) - ->method('get') - ->with($searchTerm) - ->willReturn($singleGroup); - } - - if ($shareWithGroupOnly) { - $user = $this->getUserMock('admin', 'Administrator'); - $this->session->expects($this->any()) - ->method('getUser') - ->willReturn($user); - - $numGetUserGroupsCalls = empty($groupResponse) ? 0 : 1; - $this->groupManager->expects($this->exactly($numGetUserGroupsCalls)) - ->method('getUserGroups') - ->with($user) - ->willReturn($userGroupsResponse); - } - - $this->invokePrivate($this->sharees, 'getGroups', [$searchTerm]); - $result = $this->invokePrivate($this->sharees, 'result'); - - $this->assertEquals($exactExpected, $result['exact']['groups']); - $this->assertEquals($expected, $result['groups']); - $this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor')); - } - - /** - * @dataProvider dataGetRemote - * - * @param string $searchTerm - * @param array $contacts - * @param bool $shareeEnumeration - * @param array $expected - * @param bool $reachedEnd - */ - public function testGetRemote($searchTerm, $contacts, $shareeEnumeration, $expected, $reachedEnd) { - $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); - $this->contactsManager->expects($this->any()) - ->method('search') - ->with($searchTerm, ['CLOUD', 'FN']) - ->willReturn($contacts); - - $result = $this->invokePrivate($this->sharees, 'getRemote', [$searchTerm]); - - $this->assertEquals($expected, $result); - $this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor')); - } - - public function dataGetRemote() { - return [ - ['test', [], true, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true], - ['test', [], false, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true], - [ - 'test@remote', - [], - true, - ['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false], - true, - ], - [ - 'test@remote', - [], - false, - ['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false], - true, - ], - [ - 'test', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'CLOUD' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'CLOUD' => [ - 'username@localhost', - ], - ], - ], - true, - ['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => [], 'exactIdMatch' => false], - true, - ], - [ - 'test', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'CLOUD' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'CLOUD' => [ - 'username@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [], 'exactIdMatch' => false], - true, - ], - [ - 'test@remote', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'CLOUD' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'CLOUD' => [ - 'username@localhost', - ], - ], - ], - true, - ['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false], - true, - ], - [ - 'test@remote', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'CLOUD' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'CLOUD' => [ - 'username@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false], - true, - ], - [ - 'username@localhost', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'CLOUD' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'CLOUD' => [ - 'username@localhost', - ], - ], - ], - true, - ['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true], - true, - ], - [ - 'username@localhost', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'CLOUD' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'CLOUD' => [ - 'username@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true], - true, - ], - // contact with space - [ - 'user name@localhost', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'CLOUD' => [ - ], - ], - [ - 'FN' => 'User Name @ Localhost', - 'CLOUD' => [ - 'user name@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true], - true, - ], - // remote with space, no contact - [ - 'user space@remote', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'CLOUD' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'CLOUD' => [ - 'username@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [['label' => 'user space@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user space@remote']]], 'exactIdMatch' => false], - true, - ], - ]; - } - - /** - * @dataProvider dataGetEmail - * - * @param string $searchTerm - * @param array $contacts - * @param bool $shareeEnumeration - * @param array $expected - * @param bool $reachedEnd - */ - public function testGetEmail($searchTerm, $contacts, $shareeEnumeration, $expected, $reachedEnd) { - $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); - $this->contactsManager->expects($this->any()) - ->method('search') - ->with($searchTerm, ['EMAIL', 'FN']) - ->willReturn($contacts); - - $result = $this->invokePrivate($this->sharees, 'getEmail', [$searchTerm]); - - $this->assertEquals($expected, $result); - $this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor')); - } - - public function dataGetEmail() { - return [ - ['test', [], true, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true], - ['test', [], false, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true], - [ - 'test@remote.com', - [], - true, - ['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false], - true, - ], - [ // no valid email address - 'test@remote', - [], - true, - ['results' => [], 'exact' => [], 'exactIdMatch' => false], - true, - ], - [ - 'test@remote.com', - [], - false, - ['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false], - true, - ], - [ - 'test', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'EMAIL' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'EMAIL' => [ - 'username@localhost', - ], - ], - ], - true, - ['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => [], 'exactIdMatch' => false], - true, - ], - [ - 'test', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'EMAIL' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'EMAIL' => [ - 'username@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [], 'exactIdMatch' => false], - true, - ], - [ - 'test@remote.com', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'EMAIL' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'EMAIL' => [ - 'username@localhost', - ], - ], - ], - true, - ['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false], - true, - ], - [ - 'test@remote.com', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'EMAIL' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'EMAIL' => [ - 'username@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false], - true, - ], - [ - 'username@localhost', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'EMAIL' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'EMAIL' => [ - 'username@localhost', - ], - ], - ], - true, - ['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exactIdMatch' => true], - true, - ], - [ - 'username@localhost', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'EMAIL' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'EMAIL' => [ - 'username@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exactIdMatch' => true], - true, - ], - // contact with space - [ - 'user name@localhost', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'EMAIL' => [ - ], - ], - [ - 'FN' => 'User Name @ Localhost', - 'EMAIL' => [ - 'user name@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'user name@localhost']]], 'exactIdMatch' => true], - true, - ], - // remote with space, no contact - [ - 'user space@remote.com', - [ - [ - 'FN' => 'User3 @ Localhost', - ], - [ - 'FN' => 'User2 @ Localhost', - 'EMAIL' => [ - ], - ], - [ - 'FN' => 'User @ Localhost', - 'EMAIL' => [ - 'username@localhost', - ], - ], - ], - false, - ['results' => [], 'exact' => [], 'exactIdMatch' => false], - true, - ], - // Local user found by email - [ - 'test@example.com', - [ - [ - 'FN' => 'User', - 'EMAIL' => ['test@example.com'], - 'CLOUD' => ['test@localhost'], - 'isLocalSystemBook' => true, - ] - ], - false, - ['results' => [], 'exact' => [], 'exactIdMatch' => true], - false, - ] - ]; - } - public function dataSearch() { $noRemote = [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_EMAIL]; $allTypes = [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE, Share::SHARE_TYPE_EMAIL]; @@ -1424,14 +214,13 @@ public function dataSearch() { */ public function testSearch($getData, $apiSetting, $enumSetting, $remoteSharingEnabled, $emailSharingEnabled, $shareTypes, $shareWithGroupOnly, $shareeEnumeration, $allowGroupSharing) { $search = isset($getData['search']) ? $getData['search'] : ''; - $itemType = isset($getData['itemType']) ? $getData['itemType'] : null; + $itemType = isset($getData['itemType']) ? $getData['itemType'] : 'irrelevant'; $page = isset($getData['page']) ? $getData['page'] : 1; $perPage = isset($getData['perPage']) ? $getData['perPage'] : 200; $shareType = isset($getData['shareType']) ? $getData['shareType'] : null; - $config = $this->getMockBuilder('OCP\IConfig') - ->disableOriginalConstructor() - ->getMock(); + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject $config */ + $config = $this->createMock(IConfig::class); $config->expects($this->exactly(2)) ->method('getAppValue') ->with('core', $this->anything(), $this->anything()) @@ -1444,41 +233,29 @@ public function testSearch($getData, $apiSetting, $enumSetting, $remoteSharingEn ->method('allowGroupSharing') ->willReturn($allowGroupSharing); + /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject $request */ + $request = $this->createMock(IRequest::class); + /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject $urlGenerator */ + $urlGenerator = $this->createMock(IURLGenerator::class); + /** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Files_Sharing\Controller\ShareesAPIController $sharees */ $sharees = $this->getMockBuilder('\OCA\Files_Sharing\Controller\ShareesAPIController') ->setConstructorArgs([ 'files_sharing', - $this->getMockBuilder('OCP\IRequest')->disableOriginalConstructor()->getMock(), - $this->groupManager, - $this->userManager, - $this->contactsManager, + $request, $config, - $this->session, - $this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(), - $this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(), + $urlGenerator, $this->shareManager, - $this->clientService, - $this->cloudIdManager + $this->collaboratorSearch ]) - ->setMethods(array('searchSharees', 'isRemoteSharingAllowed', 'shareProviderExists')) + ->setMethods(['isRemoteSharingAllowed', 'shareProviderExists']) ->getMock(); - $sharees->expects($this->once()) - ->method('searchSharees') - ->willReturnCallback(function - ($isearch, $iitemType, $ishareTypes, $ipage, $iperPage) - use ($search, $itemType, $shareTypes, $page, $perPage) { - // We are doing strict comparisons here, so we can differ 0/'' and null on shareType/itemType - $this->assertSame($search, $isearch); - $this->assertSame($itemType, $iitemType); - $this->assertSame(count($shareTypes), count($ishareTypes)); - foreach($shareTypes as $expected) { - $this->assertTrue(in_array($expected, $ishareTypes)); - } - $this->assertSame($page, $ipage); - $this->assertSame($perPage, $iperPage); - return new Http\DataResponse(); - }); + $this->collaboratorSearch->expects($this->once()) + ->method('search') + ->with($search, $shareTypes, $this->anything(), $perPage, $perPage * ($page -1)) + ->willReturn([[], false]); + $sharees->expects($this->any()) ->method('isRemoteSharingAllowed') ->with($itemType) @@ -1531,35 +308,34 @@ public function testSearchInvalid($getData, $message) { $page = isset($getData['page']) ? $getData['page'] : 1; $perPage = isset($getData['perPage']) ? $getData['perPage'] : 200; - $config = $this->getMockBuilder('OCP\IConfig') - ->disableOriginalConstructor() - ->getMock(); + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject $config */ + $config = $this->createMock(IConfig::class); $config->expects($this->never()) ->method('getAppValue'); + /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject $request */ + $request = $this->createMock(IRequest::class); + /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject $urlGenerator */ + $urlGenerator = $this->createMock(IURLGenerator::class); + /** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Files_Sharing\Controller\ShareesAPIController $sharees */ $sharees = $this->getMockBuilder('\OCA\Files_Sharing\Controller\ShareesAPIController') ->setConstructorArgs([ 'files_sharing', - $this->getMockBuilder('OCP\IRequest')->disableOriginalConstructor()->getMock(), - $this->groupManager, - $this->userManager, - $this->contactsManager, + $request, $config, - $this->session, - $this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(), - $this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(), + $urlGenerator, $this->shareManager, - $this->clientService, - $this->cloudIdManager + $this->collaboratorSearch ]) - ->setMethods(array('searchSharees', 'isRemoteSharingAllowed')) + ->setMethods(['isRemoteSharingAllowed']) ->getMock(); - $sharees->expects($this->never()) - ->method('searchSharees'); $sharees->expects($this->never()) ->method('isRemoteSharingAllowed'); + $this->collaboratorSearch->expects($this->never()) + ->method('search'); + try { $sharees->search('', null, $page, $perPage, null); $this->fail(); @@ -1587,188 +363,12 @@ public function testIsRemoteSharingAllowed($itemType, $expected) { $this->assertSame($expected, $this->invokePrivate($this->sharees, 'isRemoteSharingAllowed', [$itemType])); } - public function dataSearchSharees() { - return [ - ['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false], - [ - 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'circles' => [], 'emails' => []], - 'users' => [], - 'groups' => [], - 'remotes' => [], - 'emails' => [], - 'circles' => [], - 'lookup' => [], - ], false], - ['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false], - [ - 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'circles' => [], 'emails' => []], - 'users' => [], - 'groups' => [], - 'remotes' => [], - 'emails' => [], - 'circles' => [], - 'lookup' => [], - ], false], - [ - 'test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [ - ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ], [ - ['label' => 'testgroup1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'testgroup1']], - ], [ - 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false, - ], - [ - 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'circles' => [], 'emails' => []], - 'users' => [ - ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ], - 'groups' => [ - ['label' => 'testgroup1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'testgroup1']], - ], - 'remotes' => [ - ['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']], - ], - 'emails' => [], - 'circles' => [], - 'lookup' => [], - ], true, - ], - // No groups requested - [ - 'test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_REMOTE], 1, 2, false, [ - ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ], null, [ - 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false - ], - [ - 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'circles' => [], 'emails' => []], - 'users' => [ - ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ], - 'groups' => [], - 'remotes' => [ - ['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']], - ], - 'emails' => [], - 'circles' => [], - 'lookup' => [], - ], false, - ], - // Share type restricted to user - Only one user - [ - 'test', 'folder', [Share::SHARE_TYPE_USER], 1, 2, false, [ - ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ], null, null, - [ - 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'circles' => [], 'emails' => []], - 'users' => [ - ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ], - 'groups' => [], - 'remotes' => [], - 'emails' => [], - 'circles' => [], - 'lookup' => [], - ], false, - ], - // Share type restricted to user - Multipage result - [ - 'test', 'folder', [Share::SHARE_TYPE_USER], 1, 2, false, [ - ['label' => 'test 1', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ['label' => 'test 2', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], - ], null, null, - [ - 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'circles' => [], 'emails' => []], - 'users' => [ - ['label' => 'test 1', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], - ['label' => 'test 2', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], - ], - 'groups' => [], - 'remotes' => [], - 'emails' => [], - 'circles' => [], - 'lookup' => [], - ], true, - ], - ]; - } - - /** - * @dataProvider dataSearchSharees - * - * @param string $searchTerm - * @param string $itemType - * @param array $shareTypes - * @param int $page - * @param int $perPage - * @param bool $shareWithGroupOnly - * @param array $mockedUserResult - * @param array $mockedGroupsResult - * @param array $mockedRemotesResult - * @param array $expected - * @param bool $nextLink - */ - public function testSearchSharees($searchTerm, $itemType, array $shareTypes, $page, $perPage, $shareWithGroupOnly, - $mockedUserResult, $mockedGroupsResult, $mockedRemotesResult, $expected, $nextLink) { - /** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Files_Sharing\Controller\ShareesAPIController $sharees */ - $sharees = $this->getMockBuilder('\OCA\Files_Sharing\Controller\ShareesAPIController') - ->setConstructorArgs([ - 'files_sharing', - $this->getMockBuilder('OCP\IRequest')->disableOriginalConstructor()->getMock(), - $this->groupManager, - $this->userManager, - $this->contactsManager, - $this->getMockBuilder('OCP\IConfig')->disableOriginalConstructor()->getMock(), - $this->session, - $this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(), - $this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(), - $this->shareManager, - $this->clientService, - $this->cloudIdManager - ]) - ->setMethods(array('getShareesForShareIds', 'getUsers', 'getGroups', 'getRemote')) - ->getMock(); - $sharees->expects(($mockedUserResult === null) ? $this->never() : $this->once()) - ->method('getUsers') - ->with($searchTerm) - ->willReturnCallback(function() use ($sharees, $mockedUserResult) { - $result = $this->invokePrivate($sharees, 'result'); - $result['users'] = $mockedUserResult; - $this->invokePrivate($sharees, 'result', [$result]); - }); - $sharees->expects(($mockedGroupsResult === null) ? $this->never() : $this->once()) - ->method('getGroups') - ->with($searchTerm) - ->willReturnCallback(function() use ($sharees, $mockedGroupsResult) { - $result = $this->invokePrivate($sharees, 'result'); - $result['groups'] = $mockedGroupsResult; - $this->invokePrivate($sharees, 'result', [$result]); - }); - - $sharees->expects(($mockedRemotesResult === null) ? $this->never() : $this->once()) - ->method('getRemote') - ->with($searchTerm) - ->willReturn($mockedRemotesResult); - - $ocs = $this->invokePrivate($sharees, 'searchSharees', [$searchTerm, $itemType, $shareTypes, $page, $perPage, $shareWithGroupOnly]); - $this->assertInstanceOf('\OCP\AppFramework\Http\DataResponse', $ocs); - $this->assertEquals($expected, $ocs->getData()); - - // Check if next link is set - if ($nextLink) { - $headers = $ocs->getHeaders(); - $this->assertArrayHasKey('Link', $headers); - $this->assertStringStartsWith('<', $headers['Link']); - $this->assertStringEndsWith('>; rel="next"', $headers['Link']); - } - } - /** * @expectedException \OCP\AppFramework\OCS\OCSBadRequestException * @expectedExceptionMessage Missing itemType */ - public function testSearchShareesNoItemType() { - $this->invokePrivate($this->sharees, 'searchSharees', ['', null, [], [], 0, 0, false]); + public function testSearchNoItemType() { + $this->sharees->search('', null, 1, 10, [], false); } public function dataGetPaginationLink() { @@ -1814,97 +414,4 @@ public function testIsV2($scriptName, $expected) { $this->assertEquals($expected, $this->invokePrivate($this->sharees, 'isV2')); } - - /** - * @dataProvider dataTestSplitUserRemote - * - * @param string $remote - * @param string $expectedUser - * @param string $expectedUrl - */ - public function testSplitUserRemote($remote, $expectedUser, $expectedUrl) { - list($remoteUser, $remoteUrl) = $this->sharees->splitUserRemote($remote); - $this->assertSame($expectedUser, $remoteUser); - $this->assertSame($expectedUrl, $remoteUrl); - } - - public function dataTestSplitUserRemote() { - $userPrefix = ['user@name', 'username']; - $protocols = ['', 'http://', 'https://']; - $remotes = [ - 'localhost', - 'local.host', - 'dev.local.host', - 'dev.local.host/path', - 'dev.local.host/at@inpath', - '127.0.0.1', - '::1', - '::192.0.2.128', - '::192.0.2.128/at@inpath', - ]; - - $testCases = []; - foreach ($userPrefix as $user) { - foreach ($remotes as $remote) { - foreach ($protocols as $protocol) { - $baseUrl = $user . '@' . $protocol . $remote; - - $testCases[] = [$baseUrl, $user, $protocol . $remote]; - $testCases[] = [$baseUrl . '/', $user, $protocol . $remote]; - $testCases[] = [$baseUrl . '/index.php', $user, $protocol . $remote]; - $testCases[] = [$baseUrl . '/index.php/s/token', $user, $protocol . $remote]; - } - } - } - return $testCases; - } - - public function dataTestSplitUserRemoteError() { - return array( - // Invalid path - array('user@'), - - // Invalid user - array('@server'), - array('us/er@server'), - array('us:er@server'), - - // Invalid splitting - array('user'), - array(''), - array('us/erserver'), - array('us:erserver'), - ); - } - - /** - * @dataProvider dataTestSplitUserRemoteError - * - * @param string $id - * @expectedException \Exception - */ - public function testSplitUserRemoteError($id) { - $this->sharees->splitUserRemote($id); - } - - /** - * @dataProvider dataTestFixRemoteUrl - * - * @param string $url - * @param string $expected - */ - public function testFixRemoteUrl($url, $expected) { - $this->assertSame($expected, - $this->invokePrivate($this->sharees, 'fixRemoteURL', [$url]) - ); - } - - public function dataTestFixRemoteUrl() { - return [ - ['http://localhost', 'http://localhost'], - ['http://localhost/', 'http://localhost'], - ['http://localhost/index.php', 'http://localhost'], - ['http://localhost/index.php/s/AShareToken', 'http://localhost'], - ]; - } } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 50bd6d33274f..9341bd1a7624 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -68,6 +68,10 @@ 'OCP\\BackgroundJob\\IJobList' => $baseDir . '/lib/public/BackgroundJob/IJobList.php', 'OCP\\Capabilities\\ICapability' => $baseDir . '/lib/public/Capabilities/ICapability.php', 'OCP\\Capabilities\\IPublicCapability' => $baseDir . '/lib/public/Capabilities/IPublicCapability.php', + 'OCP\\Collaboration\\Collaborators\\ISearch' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearch.php', + 'OCP\\Collaboration\\Collaborators\\ISearchPlugin' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearchPlugin.php', + 'OCP\\Collaboration\\Collaborators\\ISearchResult' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearchResult.php', + 'OCP\\Collaboration\\Collaborators\\SearchResultType' => $baseDir . '/lib/public/Collaboration/Collaborators/SearchResultType.php', 'OCP\\Command\\IBus' => $baseDir . '/lib/public/Command/IBus.php', 'OCP\\Command\\ICommand' => $baseDir . '/lib/public/Command/ICommand.php', 'OCP\\Comments\\CommentsEntityEvent' => $baseDir . '/lib/public/Comments/CommentsEntityEvent.php', @@ -384,6 +388,13 @@ 'OC\\Cache\\CappedMemoryCache' => $baseDir . '/lib/private/Cache/CappedMemoryCache.php', 'OC\\Cache\\File' => $baseDir . '/lib/private/Cache/File.php', 'OC\\CapabilitiesManager' => $baseDir . '/lib/private/CapabilitiesManager.php', + 'OC\\Collaboration\\Collaborators\\GroupPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/GroupPlugin.php', + 'OC\\Collaboration\\Collaborators\\LookupPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/LookupPlugin.php', + 'OC\\Collaboration\\Collaborators\\MailPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/MailPlugin.php', + 'OC\\Collaboration\\Collaborators\\RemotePlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/RemotePlugin.php', + 'OC\\Collaboration\\Collaborators\\Search' => $baseDir . '/lib/private/Collaboration/Collaborators/Search.php', + 'OC\\Collaboration\\Collaborators\\SearchResult' => $baseDir . '/lib/private/Collaboration/Collaborators/SearchResult.php', + 'OC\\Collaboration\\Collaborators\\UserPlugin' => $baseDir . '/lib/private/Collaboration/Collaborators/UserPlugin.php', 'OC\\Command\\AsyncBus' => $baseDir . '/lib/private/Command/AsyncBus.php', 'OC\\Command\\CallableJob' => $baseDir . '/lib/private/Command/CallableJob.php', 'OC\\Command\\ClosureJob' => $baseDir . '/lib/private/Command/ClosureJob.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 1828957b32aa..5715ff6ed05e 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -98,6 +98,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\BackgroundJob\\IJobList' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/IJobList.php', 'OCP\\Capabilities\\ICapability' => __DIR__ . '/../../..' . '/lib/public/Capabilities/ICapability.php', 'OCP\\Capabilities\\IPublicCapability' => __DIR__ . '/../../..' . '/lib/public/Capabilities/IPublicCapability.php', + 'OCP\\Collaboration\\Collaborators\\ISearch' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearch.php', + 'OCP\\Collaboration\\Collaborators\\ISearchPlugin' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearchPlugin.php', + 'OCP\\Collaboration\\Collaborators\\ISearchResult' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearchResult.php', + 'OCP\\Collaboration\\Collaborators\\SearchResultType' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/SearchResultType.php', 'OCP\\Command\\IBus' => __DIR__ . '/../../..' . '/lib/public/Command/IBus.php', 'OCP\\Command\\ICommand' => __DIR__ . '/../../..' . '/lib/public/Command/ICommand.php', 'OCP\\Comments\\CommentsEntityEvent' => __DIR__ . '/../../..' . '/lib/public/Comments/CommentsEntityEvent.php', @@ -414,6 +418,13 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/private/Cache/CappedMemoryCache.php', 'OC\\Cache\\File' => __DIR__ . '/../../..' . '/lib/private/Cache/File.php', 'OC\\CapabilitiesManager' => __DIR__ . '/../../..' . '/lib/private/CapabilitiesManager.php', + 'OC\\Collaboration\\Collaborators\\GroupPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/GroupPlugin.php', + 'OC\\Collaboration\\Collaborators\\LookupPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/LookupPlugin.php', + 'OC\\Collaboration\\Collaborators\\MailPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/MailPlugin.php', + 'OC\\Collaboration\\Collaborators\\RemotePlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/RemotePlugin.php', + 'OC\\Collaboration\\Collaborators\\Search' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/Search.php', + 'OC\\Collaboration\\Collaborators\\SearchResult' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/SearchResult.php', + 'OC\\Collaboration\\Collaborators\\UserPlugin' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Collaborators/UserPlugin.php', 'OC\\Command\\AsyncBus' => __DIR__ . '/../../..' . '/lib/private/Command/AsyncBus.php', 'OC\\Command\\CallableJob' => __DIR__ . '/../../..' . '/lib/private/Command/CallableJob.php', 'OC\\Command\\ClosureJob' => __DIR__ . '/../../..' . '/lib/private/Command/ClosureJob.php', diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php index fef8ab7a1e8d..0531682d67a7 100644 --- a/lib/private/App/InfoParser.php +++ b/lib/private/App/InfoParser.php @@ -165,6 +165,12 @@ public function parse($file) { if (isset($array['activity']['providers']['provider']) && is_array($array['activity']['providers']['provider'])) { $array['activity']['providers'] = $array['activity']['providers']['provider']; } + if (isset($array['collaboration']['collaborators']['searchPlugins']['searchPlugin']) + && is_array($array['collaboration']['collaborators']['searchPlugins']['searchPlugin']) + && !isset($array['collaboration']['collaborators']['searchPlugins']['searchPlugin']['class']) + ) { + $array['collaboration']['collaborators']['searchPlugins'] = $array['collaboration']['collaborators']['searchPlugins']['searchPlugin']; + } if(!is_null($this->cache)) { $this->cache->set($fileCacheKey, json_encode($array)); diff --git a/lib/private/Collaboration/Collaborators/GroupPlugin.php b/lib/private/Collaboration/Collaborators/GroupPlugin.php new file mode 100644 index 000000000000..0b2b3d71028f --- /dev/null +++ b/lib/private/Collaboration/Collaborators/GroupPlugin.php @@ -0,0 +1,124 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Collaboration\Collaborators; + +use OCP\Collaboration\Collaborators\ISearchPlugin; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\IConfig; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\IUserSession; +use OCP\Share; + +class GroupPlugin implements ISearchPlugin { + protected $shareeEnumeration; + protected $shareWithGroupOnly; + + /** @var IGroupManager */ + private $groupManager; + /** @var IConfig */ + private $config; + /** @var IUserSession */ + private $userSession; + + public function __construct(IConfig $config, IGroupManager $groupManager, IUserSession $userSession) { + $this->groupManager = $groupManager; + $this->config = $config; + $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'; + } + + public function search($search, $limit, $offset, ISearchResult $searchResult) { + $hasMoreResults = false; + $result = ['wide' => [], 'exact' => []]; + + $groups = $this->groupManager->search($search, $limit, $offset); + $groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups); + + if (!$this->shareeEnumeration || sizeof($groups) < $limit) { + $hasMoreResults = true; + } + + $userGroups = []; + if (!empty($groups) && $this->shareWithGroupOnly) { + // Intersect all the groups that match with the groups this user is a member of + $userGroups = $this->groupManager->getUserGroups($this->userSession->getUser()); + $userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups); + $groupIds = array_intersect($groupIds, $userGroups); + } + + $lowerSearch = strtolower($search); + foreach ($groups as $group) { + // FIXME: use a more efficient approach + $gid = $group->getGID(); + if (!in_array($gid, $groupIds)) { + continue; + } + if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) { + $result['exact'][] = [ + 'label' => $group->getDisplayName(), + 'value' => [ + 'shareType' => Share::SHARE_TYPE_GROUP, + 'shareWith' => $gid, + ], + ]; + } else { + $result['wide'][] = [ + 'label' => $group->getDisplayName(), + 'value' => [ + 'shareType' => Share::SHARE_TYPE_GROUP, + 'shareWith' => $gid, + ], + ]; + } + } + + if ($offset === 0 && empty($result['exact'])) { + // On page one we try if the search result has a direct hit on the + // user id and if so, we add that to the exact match list + $group = $this->groupManager->get($search); + if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) { + array_push($result['exact'], [ + 'label' => $group->getDisplayName(), + 'value' => [ + 'shareType' => Share::SHARE_TYPE_GROUP, + 'shareWith' => $group->getGID(), + ], + ]); + } + } + + if (!$this->shareeEnumeration) { + $result['wide'] = []; + } + + $type = new SearchResultType('groups'); + $searchResult->addResultSet($type, $result['wide'], $result['exact']); + + return $hasMoreResults; + } +} diff --git a/lib/private/Collaboration/Collaborators/LookupPlugin.php b/lib/private/Collaboration/Collaborators/LookupPlugin.php new file mode 100644 index 000000000000..3a6a0943772a --- /dev/null +++ b/lib/private/Collaboration/Collaborators/LookupPlugin.php @@ -0,0 +1,85 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Collaboration\Collaborators; + + +use OCP\Collaboration\Collaborators\ISearchPlugin; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\Http\Client\IClientService; +use OCP\IConfig; +use OCP\Share; + +class LookupPlugin implements ISearchPlugin { + + /** @var IConfig */ + private $config; + /** @var IClientService */ + private $clientService; + + public function __construct(IConfig $config, IClientService $clientService) { + $this->config = $config; + $this->clientService = $clientService; + } + + public function search($search, $limit, $offset, ISearchResult $searchResult) { + if ($this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no') !== 'yes') { + return false; + } + + $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com'); + $lookupServerUrl = rtrim($lookupServerUrl, '/'); + $result = []; + + try { + $client = $this->clientService->newClient(); + $response = $client->get( + $lookupServerUrl . '/users?search=' . urlencode($search), + [ + 'timeout' => 10, + 'connect_timeout' => 3, + ] + ); + + $body = json_decode($response->getBody(), true); + + foreach ($body as $lookup) { + $result[] = [ + 'label' => $lookup['federationId'], + 'value' => [ + 'shareType' => Share::SHARE_TYPE_REMOTE, + 'shareWith' => $lookup['federationId'], + ], + 'extra' => $lookup, + ]; + } + } catch (\Exception $e) { + } + + $type = new SearchResultType('lookup'); + $searchResult->addResultSet($type, $result, []); + + return false; + } +} diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php new file mode 100644 index 000000000000..d28bd3692a4d --- /dev/null +++ b/lib/private/Collaboration/Collaborators/MailPlugin.php @@ -0,0 +1,164 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Collaboration\Collaborators; + + +use OCP\Collaboration\Collaborators\ISearchPlugin; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\Contacts\IManager; +use OCP\Federation\ICloudIdManager; +use OCP\IConfig; +use OCP\Share; + +class MailPlugin implements ISearchPlugin { + protected $shareeEnumeration; + + /** @var IManager */ + private $contactsManager; + /** @var ICloudIdManager */ + private $cloudIdManager; + /** @var IConfig */ + private $config; + + public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config) { + $this->contactsManager = $contactsManager; + $this->cloudIdManager = $cloudIdManager; + $this->config = $config; + + $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; + } + + /** + * @param $search + * @param $limit + * @param $offset + * @param ISearchResult $searchResult + * @return bool + * @since 13.0.0 + */ + public function search($search, $limit, $offset, ISearchResult $searchResult) { + $result = ['wide' => [], 'exact' => []]; + $userType = new SearchResultType('users'); + $emailType = new SearchResultType('emails'); + + // Search in contacts + //@todo Pagination missing + $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']); + $lowerSearch = strtolower($search); + foreach ($addressBookContacts as $contact) { + if (isset($contact['EMAIL'])) { + $emailAddresses = $contact['EMAIL']; + if (!is_array($emailAddresses)) { + $emailAddresses = [$emailAddresses]; + } + foreach ($emailAddresses as $emailAddress) { + $exactEmailMatch = strtolower($emailAddress) === $lowerSearch; + + if (isset($contact['isLocalSystemBook'])) { + if ($exactEmailMatch) { + try { + $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); + } catch (\InvalidArgumentException $e) { + continue; + } + + if (!$searchResult->hasResult($userType, $cloud->getUser())) { + $singleResult = [[ + 'label' => $contact['FN'] . " ($emailAddress)", + 'value' => [ + 'shareType' => Share::SHARE_TYPE_USER, + 'shareWith' => $cloud->getUser(), + ], + ]]; + $searchResult->addResultSet($userType, [], $singleResult); + $searchResult->markExactIdMatch($emailType); + } + return false; + } + + if ($this->shareeEnumeration) { + try { + $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); + } catch (\InvalidArgumentException $e) { + continue; + } + + if (!$searchResult->hasResult($userType, $cloud->getUser())) { + $singleResult = [[ + 'label' => $contact['FN'] . " ($emailAddress)", + 'value' => [ + 'shareType' => Share::SHARE_TYPE_USER, + 'shareWith' => $cloud->getUser(), + ]], + ]; + $searchResult->addResultSet($userType, $singleResult, []); + } + } + continue; + } + + if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) { + if ($exactEmailMatch) { + $searchResult->markExactIdMatch($emailType); + } + $result['exact'][] = [ + 'label' => $contact['FN'] . " ($emailAddress)", + 'value' => [ + 'shareType' => Share::SHARE_TYPE_EMAIL, + 'shareWith' => $emailAddress, + ], + ]; + } else { + $result['wide'][] = [ + 'label' => $contact['FN'] . " ($emailAddress)", + 'value' => [ + 'shareType' => Share::SHARE_TYPE_EMAIL, + 'shareWith' => $emailAddress, + ], + ]; + } + } + } + } + + if (!$this->shareeEnumeration) { + $result['wide'] = []; + } + + if (!$searchResult->hasExactIdMatch($emailType) && filter_var($search, FILTER_VALIDATE_EMAIL)) { + $result['exact'][] = [ + 'label' => $search, + 'value' => [ + 'shareType' => Share::SHARE_TYPE_EMAIL, + 'shareWith' => $search, + ], + ]; + } + + $searchResult->addResultSet($emailType, $result['wide'], $result['exact']); + + return true; + } +} diff --git a/lib/private/Collaboration/Collaborators/RemotePlugin.php b/lib/private/Collaboration/Collaborators/RemotePlugin.php new file mode 100644 index 000000000000..b17a64e4ff14 --- /dev/null +++ b/lib/private/Collaboration/Collaborators/RemotePlugin.php @@ -0,0 +1,137 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Collaboration\Collaborators; + + +use OCP\Collaboration\Collaborators\ISearchPlugin; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\Contacts\IManager; +use OCP\Federation\ICloudIdManager; +use OCP\IConfig; +use OCP\Share; + +class RemotePlugin implements ISearchPlugin { + protected $shareeEnumeration; + + /** @var IManager */ + private $contactsManager; + /** @var ICloudIdManager */ + private $cloudIdManager; + /** @var IConfig */ + private $config; + + public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config) { + $this->contactsManager = $contactsManager; + $this->cloudIdManager = $cloudIdManager; + $this->config = $config; + + $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; + } + + public function search($search, $limit, $offset, ISearchResult $searchResult) { + $result = ['wide' => [], 'exact' => []]; + $resultType = new SearchResultType('remotes'); + + // Search in contacts + //@todo Pagination missing + $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']); + foreach ($addressBookContacts as $contact) { + if (isset($contact['isLocalSystemBook'])) { + continue; + } + if (isset($contact['CLOUD'])) { + $cloudIds = $contact['CLOUD']; + if (!is_array($cloudIds)) { + $cloudIds = [$cloudIds]; + } + $lowerSearch = strtolower($search); + foreach ($cloudIds as $cloudId) { + try { + list(, $serverUrl) = $this->splitUserRemote($cloudId); + } catch (\InvalidArgumentException $e) { + continue; + } + + if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) { + if (strtolower($cloudId) === $lowerSearch) { + $searchResult->markExactIdMatch($resultType); + } + $result['exact'][] = [ + 'label' => $contact['FN'] . " ($cloudId)", + 'value' => [ + 'shareType' => Share::SHARE_TYPE_REMOTE, + 'shareWith' => $cloudId, + 'server' => $serverUrl, + ], + ]; + } else { + $result['wide'][] = [ + 'label' => $contact['FN'] . " ($cloudId)", + 'value' => [ + 'shareType' => Share::SHARE_TYPE_REMOTE, + 'shareWith' => $cloudId, + 'server' => $serverUrl, + ], + ]; + } + } + } + } + + if (!$this->shareeEnumeration) { + $result['wide'] = []; + } + + if (!$searchResult->hasExactIdMatch($resultType) && $this->cloudIdManager->isValidCloudId($search) && $offset === 0) { + $result['exact'][] = [ + 'label' => $search, + 'value' => [ + 'shareType' => Share::SHARE_TYPE_REMOTE, + 'shareWith' => $search, + ], + ]; + } + + $searchResult->addResultSet($resultType, $result['wide'], $result['exact']); + + return true; + } + + /** + * split user and remote from federated cloud id + * + * @param string $address federated share address + * @return array [user, remoteURL] + * @throws \InvalidArgumentException + */ + public function splitUserRemote($address) { + try { + $cloudId = $this->cloudIdManager->resolveCloudId($address); + return [$cloudId->getUser(), $cloudId->getRemote()]; + } catch (\InvalidArgumentException $e) { + throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e); + } + } +} diff --git a/lib/private/Collaboration/Collaborators/Search.php b/lib/private/Collaboration/Collaborators/Search.php new file mode 100644 index 000000000000..e9b15dd1201a --- /dev/null +++ b/lib/private/Collaboration/Collaborators/Search.php @@ -0,0 +1,89 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Collaboration\Collaborators; + +use OCP\Collaboration\Collaborators\ISearch; +use OCP\Collaboration\Collaborators\ISearchPlugin; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\IContainer; +use OCP\Share; + +class Search implements ISearch { + /** @var IContainer */ + private $c; + + protected $pluginList = []; + + public function __construct(IContainer $c) { + $this->c = $c; + } + + public function search($search, array $shareTypes, $lookup, $limit, $offset) { + $hasMoreResults = false; + + /** @var ISearchResult $searchResult */ + $searchResult = $this->c->resolve(SearchResult::class); + + foreach ($shareTypes as $type) { + if(!isset($this->pluginList[$type])) { + continue; + } + foreach ($this->pluginList[$type] as $plugin) { + /** @var ISearchPlugin $searchPlugin */ + $searchPlugin = $this->c->resolve($plugin); + $hasMoreResults |= $searchPlugin->search($search, $limit, $offset, $searchResult); + } + } + + // Get from lookup server, not a separate share type + if ($lookup) { + $searchPlugin = $this->c->resolve(LookupPlugin::class); + $hasMoreResults |= $searchPlugin->search($search, $limit, $offset, $searchResult); + } + + // sanitizing, could go into the plugins as well + + // if we have a exact match, either for the federated cloud id or for the + // email address we only return the exact match. It is highly unlikely + // that the exact same email address and federated cloud id exists + $emailType = new SearchResultType('emails'); + $remoteType = new SearchResultType('remotes'); + if($searchResult->hasExactIdMatch($emailType) && !$searchResult->hasExactIdMatch($remoteType)) { + $searchResult->unsetResult($remoteType); + } elseif (!$searchResult->hasExactIdMatch($emailType) && $searchResult->hasExactIdMatch($remoteType)) { + $searchResult->unsetResult($emailType); + } + + return [$searchResult->asArray(), (bool)$hasMoreResults]; + } + + public function registerPlugin(array $pluginInfo) { + $shareType = constant(Share::class . '::' . $pluginInfo['shareType']); + if($shareType === null) { + throw new \InvalidArgumentException('Provided ShareType is invalid'); + } + $this->pluginList[$shareType][] = $pluginInfo['class']; + } +} diff --git a/lib/private/Collaboration/Collaborators/SearchResult.php b/lib/private/Collaboration/Collaborators/SearchResult.php new file mode 100644 index 000000000000..7b32b388203b --- /dev/null +++ b/lib/private/Collaboration/Collaborators/SearchResult.php @@ -0,0 +1,86 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Collaboration\Collaborators; + + +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\Collaboration\Collaborators\SearchResultType; + +class SearchResult implements ISearchResult { + + protected $result = [ + 'exact' => [], + ]; + + protected $exactIdMatches = []; + + public function addResultSet(SearchResultType $type, array $matches, array $exactMatches = null) { + $type = $type->getLabel(); + if(!isset($this->result[$type])) { + $this->result[$type] = []; + $this->result['exact'][$type] = []; + } + + $this->result[$type] = array_merge($this->result[$type], $matches); + if(is_array($exactMatches)) { + $this->result['exact'][$type] = array_merge($this->result['exact'][$type], $exactMatches); + } + } + + public function markExactIdMatch(SearchResultType $type) { + $this->exactIdMatches[$type->getLabel()] = 1; + } + + public function hasExactIdMatch(SearchResultType$type) { + return isset($this->exactIdMatches[$type->getLabel()]); + } + + public function hasResult(SearchResultType $type, $collaboratorId) { + $type = $type->getLabel(); + if(!isset($this->result[$type])) { + return false; + } + + $resultArrays = [$this->result['exact'][$type], $this->result[$type]]; + foreach($resultArrays as $resultArray) { + if ($resultArray['value']['shareWith'] === $collaboratorId) { + return true; + } + } + + return false; + } + + public function asArray() { + return $this->result; + } + + public function unsetResult(SearchResultType $type) { + $type = $type->getLabel(); + $this->result[$type] = []; + if(isset($this->result['exact'][$type])) { + $this->result['exact'][$type] = []; + } + } +} diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php new file mode 100644 index 000000000000..86a55aa428fe --- /dev/null +++ b/lib/private/Collaboration/Collaborators/UserPlugin.php @@ -0,0 +1,149 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Collaboration\Collaborators; + + +use OCP\Collaboration\Collaborators\ISearchPlugin; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserManager; +use OCP\IUserSession; +use OCP\Share; + +class UserPlugin implements ISearchPlugin { + /* @var bool */ + protected $shareWithGroupOnly; + protected $shareeEnumeration; + + /** @var IConfig */ + private $config; + /** @var IGroupManager */ + private $groupManager; + /** @var IUserSession */ + private $userSession; + /** @var IUserManager */ + private $userManager; + + public function __construct(IConfig $config, IUserManager $userManager, IGroupManager $groupManager, IUserSession $userSession) { + $this->config = $config; + + $this->groupManager = $groupManager; + $this->userSession = $userSession; + $this->userManager = $userManager; + + $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'; + } + + public function search($search, $limit, $offset, ISearchResult $searchResult) { + $result = ['wide' => [], 'exact' => []]; + $users = []; + $hasMoreResults = false; + + $userGroups = []; + if ($this->shareWithGroupOnly) { + // Search in all the groups this user is part of + $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); + foreach ($userGroups as $userGroup) { + $usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $limit, $offset); + foreach ($usersTmp as $uid => $userDisplayName) { + $users[$uid] = $userDisplayName; + } + } + } else { + // Search in all users + $usersTmp = $this->userManager->searchDisplayName($search, $limit, $offset); + + foreach ($usersTmp as $user) { + $users[$user->getUID()] = $user->getDisplayName(); + } + } + + if (!$this->shareeEnumeration || sizeof($users) < $limit) { + $hasMoreResults = true; + } + + $foundUserById = false; + $lowerSearch = strtolower($search); + foreach ($users as $uid => $userDisplayName) { + if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) { + if (strtolower($uid) === $lowerSearch) { + $foundUserById = true; + } + $result['exact'][] = [ + 'label' => $userDisplayName, + 'value' => [ + 'shareType' => Share::SHARE_TYPE_USER, + 'shareWith' => $uid, + ], + ]; + } else { + $result['wide'][] = [ + 'label' => $userDisplayName, + 'value' => [ + 'shareType' => Share::SHARE_TYPE_USER, + 'shareWith' => $uid, + ], + ]; + } + } + + if ($offset === 0 && !$foundUserById) { + // On page one we try if the search result has a direct hit on the + // user id and if so, we add that to the exact match list + $user = $this->userManager->get($search); + if ($user instanceof IUser) { + $addUser = true; + + if ($this->shareWithGroupOnly) { + // Only add, if we have a common group + $commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user)); + $addUser = !empty($commonGroups); + } + + if ($addUser) { + array_push($result['exact'], [ + 'label' => $user->getDisplayName(), + 'value' => [ + 'shareType' => Share::SHARE_TYPE_USER, + 'shareWith' => $user->getUID(), + ], + ]); + } + } + } + + if (!$this->shareeEnumeration) { + $result['wide'] = []; + } + + $type = new SearchResultType('users'); + $searchResult->addResultSet($type, $result['wide'], $result['exact']); + + return $hasMoreResults; + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index a20d9ccfc011..29aee06d896e 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -52,6 +52,10 @@ use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; use OC\Authentication\LoginCredentials\Store; +use OC\Collaboration\Collaborators\GroupPlugin; +use OC\Collaboration\Collaborators\MailPlugin; +use OC\Collaboration\Collaborators\RemotePlugin; +use OC\Collaboration\Collaborators\UserPlugin; use OC\Command\CronBus; use OC\Contacts\ContactsMenu\ActionFactory; use OC\Diagnostics\EventLogger; @@ -115,6 +119,7 @@ use OCP\Lock\ILockingProvider; use OCP\RichObjectStrings\IValidator; use OCP\Security\IContentSecurityPolicyManager; +use OCP\Share; use OCP\Share\IShareHelper; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -993,6 +998,19 @@ public function __construct($webRoot, \OC\Config $config) { }); $this->registerAlias('ShareManager', \OCP\Share\IManager::class); + $this->registerService(\OCP\Collaboration\Collaborators\ISearch::class, function(Server $c) { + $instance = new Collaboration\Collaborators\Search($c); + + // register default plugins + $instance->registerPlugin(['shareType' => 'SHARE_TYPE_USER', 'class' => UserPlugin::class]); + $instance->registerPlugin(['shareType' => 'SHARE_TYPE_GROUP', 'class' => GroupPlugin::class]); + $instance->registerPlugin(['shareType' => 'SHARE_TYPE_EMAIL', 'class' => MailPlugin::class]); + $instance->registerPlugin(['shareType' => 'SHARE_TYPE_REMOTE', 'class' => RemotePlugin::class]); + + return $instance; + }); + $this->registerAlias('CollaboratorSearch', \OCP\Collaboration\Collaborators\ISearch::class); + $this->registerService('SettingsManager', function (Server $c) { $manager = new \OC\Settings\Manager( $c->getLogger(), @@ -1776,6 +1794,13 @@ public function getShareManager() { return $this->query('ShareManager'); } + /** + * @return \OCP\Collaboration\Collaborators\ISearch + */ + public function getCollaboratorSearch() { + return $this->query('CollaboratorSearch'); + } + /** * Returns the LDAP Provider * diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index 24c7a344e001..a33c9be20fc5 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -174,6 +174,20 @@ public static function loadApp($app) { \OC::$server->getActivityManager()->registerProvider($provider); } } + if (!empty($info['collaboration']['plugins'])) { + // deal with one or many plugin entries + $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ? + [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin']; + foreach ($plugins as $plugin) { + if($plugin['@attributes']['type'] === 'collaborator-search') { + $pluginInfo = [ + 'shareType' => $plugin['@attributes']['share-type'], + 'class' => $plugin['@value'], + ]; + \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo); + } + } + } } /** diff --git a/lib/public/Collaboration/Collaborators/ISearch.php b/lib/public/Collaboration/Collaborators/ISearch.php new file mode 100644 index 000000000000..281893908ee1 --- /dev/null +++ b/lib/public/Collaboration/Collaborators/ISearch.php @@ -0,0 +1,50 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\Collaboration\Collaborators; + +/** + * Interface ISearch + * + * @package OCP\Collaboration\Collaborators + * @since 13.0.0 + */ +interface ISearch { + /** + * @param string $search + * @param array $shareTypes + * @param bool $lookup + * @param int $limit + * @param int $offset + * @return array with two elements, 1st ISearchResult as array, 2nd a bool indicating whether more result are available + * @since 13.0.0 + */ + public function search($search, array $shareTypes, $lookup, $limit, $offset); + + /** + * @param array $pluginInfo with keys 'shareType' containing the name of a corresponding constant in \OCP\Share and + * 'class' with the class name of the plugin + * @since 13.0.0 + */ + public function registerPlugin(array $pluginInfo); +} diff --git a/lib/public/Collaboration/Collaborators/ISearchPlugin.php b/lib/public/Collaboration/Collaborators/ISearchPlugin.php new file mode 100644 index 000000000000..bc6fa53799b4 --- /dev/null +++ b/lib/public/Collaboration/Collaborators/ISearchPlugin.php @@ -0,0 +1,42 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\Collaboration\Collaborators; + +/** + * Interface ISearchPlugin + * + * @package OCP\Collaboration\Collaborators + * @since 13.0.0 + */ +interface ISearchPlugin { + /** + * @param string $search + * @param int $limit + * @param int $offset + * @param ISearchResult $searchResult + * @return bool whether the plugin has more results + * @since 13.0.0 + */ + public function search($search, $limit, $offset, ISearchResult $searchResult); +} diff --git a/lib/public/Collaboration/Collaborators/ISearchResult.php b/lib/public/Collaboration/Collaborators/ISearchResult.php new file mode 100644 index 000000000000..abea5df8598f --- /dev/null +++ b/lib/public/Collaboration/Collaborators/ISearchResult.php @@ -0,0 +1,73 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\Collaboration\Collaborators; + +/** + * Interface ISearchResult + * + * @package OCP\Collaboration\Collaborators + * @since 13.0.0 + */ +interface ISearchResult { + /** + * @param SearchResultType $type + * @param array $matches + * @param array|null $exactMatches + * @since 13.0.0 + */ + public function addResultSet(SearchResultType $type, array $matches, array $exactMatches = null); + + /** + * @param SearchResultType $type + * @param string $collaboratorId + * @return bool + * @since 13.0.0 + */ + public function hasResult(SearchResultType $type, $collaboratorId); + + /** + * @param SearchResultType $type + * @since 13.0.0 + */ + public function unsetResult(SearchResultType $type); + + /** + * @param SearchResultType $type + * @since 13.0.0 + */ + public function markExactIdMatch(SearchResultType $type); + + /** + * @param SearchResultType $type + * @return bool + * @since 13.0.0 + */ + public function hasExactIdMatch(SearchResultType $type); + + /** + * @return array + * @since 13.0.0 + */ + public function asArray(); +} diff --git a/lib/public/Collaboration/Collaborators/SearchResultType.php b/lib/public/Collaboration/Collaborators/SearchResultType.php new file mode 100644 index 000000000000..e4ad888d6e4d --- /dev/null +++ b/lib/public/Collaboration/Collaborators/SearchResultType.php @@ -0,0 +1,73 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\Collaboration\Collaborators; + +/** + * Class SearchResultType + * + * @package OCP\Collaboration\Collaborators + * @since 13.0.0 + */ +class SearchResultType { + /** @var string */ + protected $label; + + /** + * SearchResultType constructor. + * + * @param string $label + * @since 13.0.0 + */ + public function __construct($label) { + $this->label = $this->getValidatedType($label); + } + + /** + * @return string + * @since 13.0.0 + */ + public function getLabel() { + return $this->label; + } + + /** + * @param $type + * @return string + * @throws \InvalidArgumentException + * @since 13.0.0 + */ + protected function getValidatedType($type) { + $type = trim(strval($type)); + + if($type === '') { + throw new \InvalidArgumentException('Type must not be empty'); + } + + if($type === 'exact') { + throw new \InvalidArgumentException('Provided type is a reserved word'); + } + + return $type; + } +} diff --git a/tests/lib/Collaboration/Collaborators/GroupPluginTest.php b/tests/lib/Collaboration/Collaborators/GroupPluginTest.php new file mode 100644 index 000000000000..9849bdb874a2 --- /dev/null +++ b/tests/lib/Collaboration/Collaborators/GroupPluginTest.php @@ -0,0 +1,491 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace Test\Collaboration\Collaborators; + + +use OC\Collaboration\Collaborators\GroupPlugin; +use OC\Collaboration\Collaborators\SearchResult; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\IConfig; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserSession; +use OCP\Share; +use Test\TestCase; + +class GroupPluginTest extends TestCase { + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + protected $config; + + /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $groupManager; + + /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */ + protected $session; + + /** @var ISearchResult */ + protected $searchResult; + + /** @var GroupPlugin */ + protected $plugin; + + /** @var int */ + protected $limit = 2; + + /** @var int */ + protected $offset = 0; + + /** @var IUser|\PHPUnit_Framework_MockObject_MockObject */ + protected $user; + + public function setUp() { + parent::setUp(); + + $this->config = $this->createMock(IConfig::class); + + $this->groupManager = $this->createMock(IGroupManager::class); + + $this->session = $this->createMock(IUserSession::class); + + $this->searchResult = new SearchResult(); + + $this->user = $this->getUserMock('admin', 'Administrator'); + } + + public function instantiatePlugin() { + // cannot be done within setUp, because dependent mocks needs to be set + // up with configuration etc. first + $this->plugin = new GroupPlugin( + $this->config, + $this->groupManager, + $this->session + ); + } + + public function getUserMock($uid, $displayName) { + $user = $this->createMock(IUser::class); + + $user->expects($this->any()) + ->method('getUID') + ->willReturn($uid); + + $user->expects($this->any()) + ->method('getDisplayName') + ->willReturn($displayName); + + return $user; + } + + /** + * @param string $gid + * @param null $displayName + * @return IGroup|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getGroupMock($gid, $displayName = null) { + $group = $this->createMock(IGroup::class); + + $group->expects($this->any()) + ->method('getGID') + ->willReturn($gid); + + if (is_null($displayName)) { + // note: this is how the Group class behaves + $displayName = $gid; + } + + $group->expects($this->any()) + ->method('getDisplayName') + ->willReturn($displayName); + + return $group; + } + + public function dataGetGroups() { + return [ + ['test', false, true, [], [], [], [], true, false], + ['test', false, false, [], [], [], [], true, false], + // group without display name + [ + 'test', false, true, + [$this->getGroupMock('test1')], + [], + [], + [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], + true, + false, + ], + // group with display name, search by id + [ + 'test', false, true, + [$this->getGroupMock('test1', 'Test One')], + [], + [], + [['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], + true, + false, + ], + // group with display name, search by display name + [ + 'one', false, true, + [$this->getGroupMock('test1', 'Test One')], + [], + [], + [['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], + true, + false, + ], + // group with display name, search by display name, exact expected + [ + 'Test One', false, true, + [$this->getGroupMock('test1', 'Test One')], + [], + [['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], + [], + true, + false, + ], + [ + 'test', false, false, + [$this->getGroupMock('test1')], + [], + [], + [], + true, + false, + ], + [ + 'test', false, true, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], + false, + false, + ], + [ + 'test', false, false, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [], + true, + false, + ], + [ + 'test', false, true, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [], + [], + [ + ['label' => 'test0', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test0']], + ['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']], + ], + false, + null, + ], + [ + 'test', false, false, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [], + [], + [], + true, + null, + ], + [ + 'test', false, true, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [], + [ + ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], + ], + [ + ['label' => 'test0', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test0']], + ['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']], + ], + false, + $this->getGroupMock('test'), + ], + [ + 'test', false, false, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [], + [ + ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], + ], + [], + true, + $this->getGroupMock('test'), + ], + ['test', true, true, [], [], [], [], true, false], + ['test', true, false, [], [], [], [], true, false], + [ + 'test', true, true, + [ + $this->getGroupMock('test1'), + $this->getGroupMock('test2'), + ], + [$this->getGroupMock('test1')], + [], + [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], + false, + false, + ], + [ + 'test', true, false, + [ + $this->getGroupMock('test1'), + $this->getGroupMock('test2'), + ], + [$this->getGroupMock('test1')], + [], + [], + true, + false, + ], + [ + 'test', true, true, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test')], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [], + false, + false, + ], + [ + 'test', true, false, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test')], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [], + true, + false, + ], + [ + 'test', true, true, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test1')], + [], + [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], + false, + false, + ], + [ + 'test', true, false, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test1')], + [], + [], + true, + false, + ], + [ + 'test', true, true, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']]], + false, + false, + ], + [ + 'test', true, false, + [ + $this->getGroupMock('test'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']]], + [], + true, + false, + ], + [ + 'test', true, true, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [], + [ + ['label' => 'test0', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test0']], + ['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']], + ], + false, + null, + ], + [ + 'test', true, false, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [], + [], + true, + null, + ], + [ + 'test', true, true, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [ + ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], + ], + [ + ['label' => 'test0', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test0']], + ['label' => 'test1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test1']], + ], + false, + $this->getGroupMock('test'), + ], + [ + 'test', true, false, + [ + $this->getGroupMock('test0'), + $this->getGroupMock('test1'), + ], + [$this->getGroupMock('test'), $this->getGroupMock('test0'), $this->getGroupMock('test1')], + [ + ['label' => 'test', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'test']], + ], + [], + true, + $this->getGroupMock('test'), + ], + ]; + } + + /** + * @dataProvider dataGetGroups + * + * @param string $searchTerm + * @param bool $shareWithGroupOnly + * @param bool $shareeEnumeration + * @param array $groupResponse + * @param array $userGroupsResponse + * @param array $exactExpected + * @param array $expected + * @param bool $reachedEnd + * @param bool|IGroup $singleGroup + */ + public function testSearch( + $searchTerm, + $shareWithGroupOnly, + $shareeEnumeration, + array $groupResponse, + array $userGroupsResponse, + array $exactExpected, + array $expected, + $reachedEnd, + $singleGroup + ) { + $this->config->expects($this->any()) + ->method('getAppValue') + ->willReturnCallback( + function($appName, $key, $default) + use ($shareWithGroupOnly, $shareeEnumeration) + { + if ($appName === 'core' && $key === 'shareapi_only_share_with_group_members') { + return $shareWithGroupOnly ? 'yes' : 'no'; + } else if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') { + return $shareeEnumeration ? 'yes' : 'no'; + } + return $default; + } + ); + + $this->instantiatePlugin(); + + $this->groupManager->expects($this->once()) + ->method('search') + ->with($searchTerm, $this->limit, $this->offset) + ->willReturn($groupResponse); + + if ($singleGroup !== false) { + $this->groupManager->expects($this->once()) + ->method('get') + ->with($searchTerm) + ->willReturn($singleGroup); + } + + if ($shareWithGroupOnly) { + $this->session->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $numGetUserGroupsCalls = empty($groupResponse) ? 0 : 1; + $this->groupManager->expects($this->exactly($numGetUserGroupsCalls)) + ->method('getUserGroups') + ->with($this->user) + ->willReturn($userGroupsResponse); + } + + $moreResults = $this->plugin->search($searchTerm, $this->limit, $this->offset, $this->searchResult); + $result = $this->searchResult->asArray(); + + $this->assertEquals($exactExpected, $result['exact']['groups']); + $this->assertEquals($expected, $result['groups']); + $this->assertSame($reachedEnd, $moreResults); + } +} diff --git a/tests/lib/Collaboration/Collaborators/LookupPluginTest.php b/tests/lib/Collaboration/Collaborators/LookupPluginTest.php new file mode 100644 index 000000000000..83d366cf4676 --- /dev/null +++ b/tests/lib/Collaboration/Collaborators/LookupPluginTest.php @@ -0,0 +1,180 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace Test\Collaboration\Collaborators; + + +use OC\Collaboration\Collaborators\LookupPlugin; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\Http\Client\IClient; +use OCP\Http\Client\IClientService; +use OCP\Http\Client\IResponse; +use OCP\IConfig; +use OCP\Share; +use Test\TestCase; + +class LookupPluginTest extends TestCase { + + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + protected $config; + /** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */ + protected $clientService; + /** @var LookupPlugin */ + protected $plugin; + + public function setUp() { + parent::setUp(); + + $this->config = $this->createMock(IConfig::class); + $this->clientService = $this->createMock(IClientService::class); + + $this->plugin = new LookupPlugin($this->config, $this->clientService); + } + + /** + * @dataProvider searchDataProvider + * @param array $searchParams + */ + public function testSearch(array $searchParams) { + $type = new SearchResultType('lookup'); + + /** @var ISearchResult|\PHPUnit_Framework_MockObject_MockObject $searchResult */ + $searchResult = $this->createMock(ISearchResult::class); + $searchResult->expects($this->once()) + ->method('addResultSet') + ->with($type, $searchParams['expectedResult'], []); + + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('files_sharing', 'lookupServerEnabled', 'no') + ->willReturn('yes'); + $this->config->expects($this->once()) + ->method('getSystemValue') + ->with('lookup_server', 'https://lookup.nextcloud.com') + ->willReturn($searchParams['server']); + + $response = $this->createMock(IResponse::class); + $response->expects($this->once()) + ->method('getBody') + ->willReturn(json_encode($searchParams['resultBody'])); + + $client = $this->createMock(IClient::class); + $client->expects($this->once()) + ->method('get') + ->willReturnCallback(function($url) use ($searchParams, $response) { + $this->assertSame(strpos($url, $searchParams['server'] . '/users?search='), 0); + $this->assertNotFalse(strpos($url, urlencode($searchParams['search']))); + return $response; + }); + + $this->clientService->expects($this->once()) + ->method('newClient') + ->willReturn($client); + + $moreResults = $this->plugin->search( + $searchParams['search'], + $searchParams['limit'], + $searchParams['offset'], + $searchResult + ); + + + + $this->assertFalse($moreResults); + } + + public function testSearchLookupServerDisabled() { + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('files_sharing', 'lookupServerEnabled', 'no') + ->willReturn('no'); + + /** @var ISearchResult|\PHPUnit_Framework_MockObject_MockObject $searchResult */ + $searchResult = $this->createMock(ISearchResult::class); + $searchResult->expects($this->never()) + ->method('addResultSet'); + $searchResult->expects($this->never()) + ->method('markExactIdMatch'); + + $this->assertFalse($this->plugin->search('irr', 10, 0, $searchResult)); + } + + public function searchDataProvider() { + $fedIDs = [ + 'foo@enceladus.moon', + 'foobar@enceladus.moon', + 'foongus@enceladus.moon', + ]; + + return [ + // #0, standard search with results + [[ + 'search' => 'foo', + 'limit' => 10, + 'offset' => 0, + 'server' => 'https://lookup.example.io', + 'resultBody' => [ + [ 'federationId' => $fedIDs[0] ], + [ 'federationId' => $fedIDs[1] ], + [ 'federationId' => $fedIDs[2] ], + ], + 'expectedResult' => [ + [ + 'label' => $fedIDs[0], + 'value' => [ + 'shareType' => Share::SHARE_TYPE_REMOTE, + 'shareWith' => $fedIDs[0] + ], + 'extra' => ['federationId' => $fedIDs[0]], + ], + [ + 'label' => $fedIDs[1], + 'value' => [ + 'shareType' => Share::SHARE_TYPE_REMOTE, + 'shareWith' => $fedIDs[1] + ], + 'extra' => ['federationId' => $fedIDs[1]], + ], + [ + 'label' => $fedIDs[2], + 'value' => [ + 'shareType' => Share::SHARE_TYPE_REMOTE, + 'shareWith' => $fedIDs[2] + ], + 'extra' => ['federationId' => $fedIDs[2]], + ], + ] + ]], + // #1, search without results + [[ + 'search' => 'foo', + 'limit' => 10, + 'offset' => 0, + 'server' => 'https://lookup.example.io', + 'resultBody' => [], + 'expectedResult' => [], + ]], + ]; + } +} diff --git a/tests/lib/Collaboration/Collaborators/MailPluginTest.php b/tests/lib/Collaboration/Collaborators/MailPluginTest.php new file mode 100644 index 000000000000..9c9d9cff9091 --- /dev/null +++ b/tests/lib/Collaboration/Collaborators/MailPluginTest.php @@ -0,0 +1,336 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace Test\Collaboration\Collaborators; + + +use OC\Collaboration\Collaborators\MailPlugin; +use OC\Collaboration\Collaborators\SearchResult; +use OC\Federation\CloudIdManager; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\Contacts\IManager; +use OCP\Federation\ICloudIdManager; +use OCP\IConfig; +use OCP\Share; +use Test\TestCase; + +class MailPluginTest extends TestCase { + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + protected $config; + + /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $contactsManager; + + /** @var ICloudIdManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $cloudIdManager; + + /** @var MailPlugin */ + protected $plugin; + + /** @var SearchResult */ + protected $searchResult; + + public function setUp() { + parent::setUp(); + + $this->config = $this->createMock(IConfig::class); + $this->contactsManager = $this->createMock(IManager::class); + $this->cloudIdManager = new CloudIdManager(); + $this->searchResult = new SearchResult(); + } + + public function instantiatePlugin() { + $this->plugin = new MailPlugin($this->contactsManager, $this->cloudIdManager, $this->config); + } + + /** + * @dataProvider dataGetEmail + * + * @param string $searchTerm + * @param array $contacts + * @param bool $shareeEnumeration + * @param array $expected + * @param bool $reachedEnd + */ + public function testSearch($searchTerm, $contacts, $shareeEnumeration, $expected, $exactIdMatch, $reachedEnd) { + $this->config->expects($this->any()) + ->method('getAppValue') + ->willReturnCallback( + function($appName, $key, $default) + use ($shareeEnumeration) + { + if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') { + return $shareeEnumeration ? 'yes' : 'no'; + } + return $default; + } + ); + + $this->instantiatePlugin(); + + $this->contactsManager->expects($this->any()) + ->method('search') + ->with($searchTerm, ['EMAIL', 'FN']) + ->willReturn($contacts); + + $moreResults = $this->plugin->search($searchTerm, 0, 0, $this->searchResult); + $result = $this->searchResult->asArray(); + + $this->assertSame($exactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails'))); + $this->assertEquals($expected, $result); + $this->assertSame($reachedEnd, $moreResults); + } + + public function dataGetEmail() { + return [ + ['test', [], true, ['emails' => [], 'exact' => ['emails' => []]], false, true], + ['test', [], false, ['emails' => [], 'exact' => ['emails' => []]], false, true], + [ + 'test@remote.com', + [], + true, + ['emails' => [], 'exact' => ['emails' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]], + false, + true, + ], + [ // no valid email address + 'test@remote', + [], + true, + ['emails' => [], 'exact' => ['emails' => []]], + false, + true, + ], + [ + 'test@remote.com', + [], + false, + ['emails' => [], 'exact' => ['emails' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]], + false, + true, + ], + [ + 'test', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'EMAIL' => [ + 'username@localhost', + ], + ], + ], + true, + ['emails' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => ['emails' => []]], + false, + true, + ], + [ + 'test', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'EMAIL' => [ + 'username@localhost', + ], + ], + ], + false, + ['emails' => [], 'exact' => ['emails' => []]], + false, + true, + ], + [ + 'test@remote.com', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'EMAIL' => [ + 'username@localhost', + ], + ], + ], + true, + ['emails' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => ['emails' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]], + false, + true, + ], + [ + 'test@remote.com', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'EMAIL' => [ + 'username@localhost', + ], + ], + ], + false, + ['emails' => [], 'exact' => ['emails' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]], + false, + true, + ], + [ + 'username@localhost', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'EMAIL' => [ + 'username@localhost', + ], + ], + ], + true, + ['emails' => [], 'exact' => ['emails' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]]]], + true, + true, + ], + [ + 'username@localhost', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'EMAIL' => [ + 'username@localhost', + ], + ], + ], + false, + ['emails' => [], 'exact' => ['emails' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]]]], + true, + true, + ], + // contact with space + [ + 'user name@localhost', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User Name @ Localhost', + 'EMAIL' => [ + 'user name@localhost', + ], + ], + ], + false, + ['emails' => [], 'exact' => ['emails' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'user name@localhost']]]]], + true, + true, + ], + // remote with space, no contact + [ + 'user space@remote.com', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'EMAIL' => [ + 'username@localhost', + ], + ], + ], + false, + ['emails' => [], 'exact' => ['emails' => []]], + false, + true, + ], + // Local user found by email + [ + 'test@example.com', + [ + [ + 'FN' => 'User', + 'EMAIL' => ['test@example.com'], + 'CLOUD' => ['test@localhost'], + 'isLocalSystemBook' => true, + ] + ], + false, + ['users' => [], 'exact' => ['users' => [['label' => 'User (test@example.com)','value' => ['shareType' => 0, 'shareWith' => 'test'],]]]], + true, + false, + ] + ]; + } +} diff --git a/tests/lib/Collaboration/Collaborators/RemotePluginTest.php b/tests/lib/Collaboration/Collaborators/RemotePluginTest.php new file mode 100644 index 000000000000..5c4b3af5e702 --- /dev/null +++ b/tests/lib/Collaboration/Collaborators/RemotePluginTest.php @@ -0,0 +1,388 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace Test\Collaboration\Collaborators; + + +use OC\Collaboration\Collaborators\RemotePlugin; +use OC\Collaboration\Collaborators\SearchResult; +use OC\Federation\CloudIdManager; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\Contacts\IManager; +use OCP\Federation\ICloudIdManager; +use OCP\IConfig; +use OCP\Share; +use Test\TestCase; + +class RemotePluginTest extends TestCase { + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + protected $config; + + /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $contactsManager; + + /** @var ICloudIdManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $cloudIdManager; + + /** @var RemotePlugin */ + protected $plugin; + + /** @var SearchResult */ + protected $searchResult; + + public function setUp() { + parent::setUp(); + + $this->config = $this->createMock(IConfig::class); + $this->contactsManager = $this->createMock(IManager::class); + $this->cloudIdManager = new CloudIdManager(); + $this->searchResult = new SearchResult(); + } + + public function instantiatePlugin() { + $this->plugin = new RemotePlugin($this->contactsManager, $this->cloudIdManager, $this->config); + } + + /** + * @dataProvider dataGetRemote + * + * @param string $searchTerm + * @param array $contacts + * @param bool $shareeEnumeration + * @param array $expected + * @param bool $exactIdMatch + * @param bool $reachedEnd + */ + public function testSearch($searchTerm, array $contacts, $shareeEnumeration, array $expected, $exactIdMatch, $reachedEnd) { + $this->config->expects($this->any()) + ->method('getAppValue') + ->willReturnCallback( + function($appName, $key, $default) + use ($shareeEnumeration) + { + if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') { + return $shareeEnumeration ? 'yes' : 'no'; + } + return $default; + } + ); + + $this->instantiatePlugin(); + + $this->contactsManager->expects($this->any()) + ->method('search') + ->with($searchTerm, ['CLOUD', 'FN']) + ->willReturn($contacts); + + $moreResults = $this->plugin->search($searchTerm, 0, 0, $this->searchResult); + $result = $this->searchResult->asArray(); + + $this->assertSame($exactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('remotes'))); + $this->assertEquals($expected, $result); + $this->assertSame($reachedEnd, $moreResults); + } + + /** + * @dataProvider dataTestSplitUserRemote + * + * @param string $remote + * @param string $expectedUser + * @param string $expectedUrl + */ + public function testSplitUserRemote($remote, $expectedUser, $expectedUrl) { + $this->instantiatePlugin(); + + list($remoteUser, $remoteUrl) = $this->plugin->splitUserRemote($remote); + $this->assertSame($expectedUser, $remoteUser); + $this->assertSame($expectedUrl, $remoteUrl); + } + + /** + * @dataProvider dataTestSplitUserRemoteError + * + * @param string $id + * @expectedException \Exception + */ + public function testSplitUserRemoteError($id) { + $this->instantiatePlugin(); + $this->plugin->splitUserRemote($id); + } + + public function dataGetRemote() { + return [ + ['test', [], true, ['remotes' => [], 'exact' => ['remotes' => []]], false, true], + ['test', [], false, ['remotes' => [], 'exact' => ['remotes' => []]], false, true], + [ + 'test@remote', + [], + true, + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]]]], + false, + true, + ], + [ + 'test@remote', + [], + false, + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]]]], + false, + true, + ], + [ + 'test', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + true, + ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => ['remotes' => []]], + false, + true, + ], + [ + 'test', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + false, + ['remotes' => [], 'exact' => ['remotes' => []]], + false, + true, + ], + [ + 'test@remote', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + true, + ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => ['remotes' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]]]], + false, + true, + ], + [ + 'test@remote', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + false, + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]]]], + false, + true, + ], + [ + 'username@localhost', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + true, + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]]]], + true, + true, + ], + [ + 'username@localhost', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + false, + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]]]], + true, + true, + ], + // contact with space + [ + 'user name@localhost', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User Name @ Localhost', + 'CLOUD' => [ + 'user name@localhost', + ], + ], + ], + false, + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']]]]], + true, + true, + ], + // remote with space, no contact + [ + 'user space@remote', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'CLOUD' => [ + ], + ], + [ + 'FN' => 'User @ Localhost', + 'CLOUD' => [ + 'username@localhost', + ], + ], + ], + false, + ['remotes' => [], 'exact' => ['remotes' => [['label' => 'user space@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user space@remote']]]]], + false, + true, + ], + ]; + } + + public function dataTestSplitUserRemote() { + $userPrefix = ['user@name', 'username']; + $protocols = ['', 'http://', 'https://']; + $remotes = [ + 'localhost', + 'local.host', + 'dev.local.host', + 'dev.local.host/path', + 'dev.local.host/at@inpath', + '127.0.0.1', + '::1', + '::192.0.2.128', + '::192.0.2.128/at@inpath', + ]; + + $testCases = []; + foreach ($userPrefix as $user) { + foreach ($remotes as $remote) { + foreach ($protocols as $protocol) { + $baseUrl = $user . '@' . $protocol . $remote; + + $testCases[] = [$baseUrl, $user, $protocol . $remote]; + $testCases[] = [$baseUrl . '/', $user, $protocol . $remote]; + $testCases[] = [$baseUrl . '/index.php', $user, $protocol . $remote]; + $testCases[] = [$baseUrl . '/index.php/s/token', $user, $protocol . $remote]; + } + } + } + return $testCases; + } + + public function dataTestSplitUserRemoteError() { + return array( + // Invalid path + array('user@'), + + // Invalid user + array('@server'), + array('us/er@server'), + array('us:er@server'), + + // Invalid splitting + array('user'), + array(''), + array('us/erserver'), + array('us:erserver'), + ); + } +} diff --git a/tests/lib/Collaboration/Collaborators/SearchTest.php b/tests/lib/Collaboration/Collaborators/SearchTest.php new file mode 100644 index 000000000000..80e6c7f0bebe --- /dev/null +++ b/tests/lib/Collaboration/Collaborators/SearchTest.php @@ -0,0 +1,219 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace Test\Collaboration\Collaborators; + + +use OC\Collaboration\Collaborators\Search; +use OC\Collaboration\Collaborators\SearchResult; +use OCP\Collaboration\Collaborators\ISearch; +use OCP\Collaboration\Collaborators\ISearchPlugin; +use OCP\Collaboration\Collaborators\SearchResultType; +use OCP\IContainer; +use OCP\Share; +use Test\TestCase; + +class SearchTest extends TestCase { + /** @var IContainer|\PHPUnit_Framework_MockObject_MockObject */ + protected $container; + /** @var ISearch */ + protected $search; + + public function setUp() { + parent::setUp(); + + $this->container = $this->createMock(IContainer::class); + + $this->search = new Search($this->container); + } + + /** + * @dataProvider dataSearchSharees + * + * @param string $searchTerm + * @param array $shareTypes + * @param int $page + * @param int $perPage + * @param array $mockedUserResult + * @param array $mockedGroupsResult + * @param array $mockedRemotesResult + * @param array $expected + * @param bool $expectedMoreResults + */ + public function testSearch( + $searchTerm, + array $shareTypes, + $page, + $perPage, + array $mockedUserResult, + array $mockedGroupsResult, + array $mockedRemotesResult, + array $expected, + $expectedMoreResults + ) { + $searchResult = new SearchResult(); + + $userPlugin = $this->createMock(ISearchPlugin::class); + $userPlugin->expects($this->any()) + ->method('search') + ->willReturnCallback(function() use ($searchResult, $mockedUserResult, $expectedMoreResults) { + $type = new SearchResultType('users'); + $searchResult->addResultSet($type, $mockedUserResult); + return $expectedMoreResults; + }); + + $groupPlugin = $this->createMock(ISearchPlugin::class); + $groupPlugin->expects($this->any()) + ->method('search') + ->willReturnCallback(function() use ($searchResult, $mockedGroupsResult, $expectedMoreResults) { + $type = new SearchResultType('groups'); + $searchResult->addResultSet($type, $mockedGroupsResult); + return $expectedMoreResults; + }); + + $remotePlugin = $this->createMock(ISearchPlugin::class); + $remotePlugin->expects($this->any()) + ->method('search') + ->willReturnCallback(function() use ($searchResult, $mockedRemotesResult, $expectedMoreResults) { + if($mockedRemotesResult !== null) { + $type = new SearchResultType('remotes'); + $searchResult->addResultSet($type, $mockedRemotesResult['results'], $mockedRemotesResult['exact']); + if($mockedRemotesResult['exactIdMatch'] === true) { + $searchResult->markExactIdMatch($type); + } + } + return $expectedMoreResults; + }); + + $this->container->expects($this->any()) + ->method('resolve') + ->willReturnCallback(function($class) use ($searchResult, $userPlugin, $groupPlugin, $remotePlugin) { + if($class === SearchResult::class) { + return $searchResult; + } elseif ($class === $userPlugin) { + return $userPlugin; + } elseif ($class === $groupPlugin) { + return $groupPlugin; + } elseif ($class === $remotePlugin) { + return $remotePlugin; + } + return null; + }); + + $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_USER', 'class' => $userPlugin]); + $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_GROUP', 'class' => $groupPlugin]); + $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_REMOTE', 'class' => $remotePlugin]); + + list($results, $moreResults) = $this->search->search($searchTerm, $shareTypes, false, $perPage, $perPage * ($page - 1)); + + $this->assertEquals($expected, $results); + $this->assertSame($expectedMoreResults, $moreResults); + } + + public function dataSearchSharees() { + return [ + [ + 'test', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false], + [ + 'exact' => ['users' => [], 'groups' => [], 'remotes' => []], + 'users' => [], + 'groups' => [], + 'remotes' => [], + ], false + ], + [ + 'test', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false], + [ + 'exact' => ['users' => [], 'groups' => [], 'remotes' => []], + 'users' => [], + 'groups' => [], + 'remotes' => [], + ], false + ], + [ + 'test', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, [ + ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ], [ + ['label' => 'testgroup1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'testgroup1']], + ], [ + 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false, + ], + [ + 'exact' => ['users' => [], 'groups' => [], 'remotes' => []], + 'users' => [ + ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ], + 'groups' => [ + ['label' => 'testgroup1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'testgroup1']], + ], + 'remotes' => [ + ['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']], + ], + ], true, + ], + // No groups requested + [ + 'test', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_REMOTE], 1, 2, [ + ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ], [], [ + 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false + ], + [ + 'exact' => ['users' => [], 'remotes' => []], + 'users' => [ + ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ], + 'remotes' => [ + ['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']], + ], + ], false, + ], + // Share type restricted to user - Only one user + [ + 'test', [Share::SHARE_TYPE_USER], 1, 2, [ + ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ], [], [], + [ + 'exact' => ['users' => []], + 'users' => [ + ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ], + ], false, + ], + // Share type restricted to user - Multipage result + [ + 'test', [Share::SHARE_TYPE_USER], 1, 2, [ + ['label' => 'test 1', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ['label' => 'test 2', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], + ], [], [], + [ + 'exact' => ['users' => []], + 'users' => [ + ['label' => 'test 1', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ['label' => 'test 2', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], + ], + ], true, + ], + ]; + } +} diff --git a/tests/lib/Collaboration/Collaborators/UserPluginTest.php b/tests/lib/Collaboration/Collaborators/UserPluginTest.php new file mode 100644 index 000000000000..7d6d9c645a0f --- /dev/null +++ b/tests/lib/Collaboration/Collaborators/UserPluginTest.php @@ -0,0 +1,445 @@ + + * + * @author Arthur Schiwon + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace Test\Collaboration\Collaborators; + +use OC\Collaboration\Collaborators\SearchResult; +use OC\Collaboration\Collaborators\UserPlugin; +use OCP\Collaboration\Collaborators\ISearchResult; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\IUserManager; +use OCP\IUserSession; +use OCP\Share; +use Test\TestCase; + +class UserPluginTest extends TestCase { + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + protected $config; + + /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $userManager; + + /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $groupManager; + + /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */ + protected $session; + + /** @var UserPlugin */ + protected $plugin; + + /** @var ISearchResult */ + protected $searchResult; + + /** @var int */ + protected $limit = 2; + + /** @var int */ + protected $offset = 0; + + /** @var IUser|\PHPUnit_Framework_MockObject_MockObject */ + protected $user; + + public function setUp() { + parent::setUp(); + + $this->config = $this->createMock(IConfig::class); + + $this->userManager = $this->createMock(IUserManager::class); + + $this->groupManager = $this->createMock(IGroupManager::class); + + $this->session = $this->createMock(IUserSession::class); + + $this->searchResult = new SearchResult(); + + $this->user = $this->getUserMock('admin', 'Administrator'); + } + + public function instantiatePlugin() { + // cannot be done within setUp, because dependent mocks needs to be set + // up with configuration etc. first + $this->plugin = new UserPlugin( + $this->config, + $this->userManager, + $this->groupManager, + $this->session + ); + } + + public function getUserMock($uid, $displayName) { + $user = $this->createMock(IUser::class); + + $user->expects($this->any()) + ->method('getUID') + ->willReturn($uid); + + $user->expects($this->any()) + ->method('getDisplayName') + ->willReturn($displayName); + + return $user; + } + + public function dataGetUsers() { + return [ + ['test', false, true, [], [], [], [], true, false], + ['test', false, false, [], [], [], [], true, false], + ['test', true, true, [], [], [], [], true, false], + ['test', true, false, [], [], [], [], true, false], + [ + 'test', false, true, [], [], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], [], true, $this->getUserMock('test', 'Test') + ], + [ + 'test', false, false, [], [], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], [], true, $this->getUserMock('test', 'Test') + ], + [ + 'test', true, true, [], [], + [], [], true, $this->getUserMock('test', 'Test') + ], + [ + 'test', true, false, [], [], + [], [], true, $this->getUserMock('test', 'Test') + ], + [ + 'test', true, true, ['test-group'], [['test-group', 'test', 2, 0, []]], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], [], true, $this->getUserMock('test', 'Test') + ], + [ + 'test', true, false, ['test-group'], [['test-group', 'test', 2, 0, []]], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], [], true, $this->getUserMock('test', 'Test') + ], + [ + 'test', + false, + true, + [], + [ + $this->getUserMock('test1', 'Test One'), + ], + [], + [ + ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ], + true, + false, + ], + [ + 'test', + false, + false, + [], + [ + $this->getUserMock('test1', 'Test One'), + ], + [], + [], + true, + false, + ], + [ + 'test', + false, + true, + [], + [ + $this->getUserMock('test1', 'Test One'), + $this->getUserMock('test2', 'Test Two'), + ], + [], + [ + ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ['label' => 'Test Two', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], + ], + false, + false, + ], + [ + 'test', + false, + false, + [], + [ + $this->getUserMock('test1', 'Test One'), + $this->getUserMock('test2', 'Test Two'), + ], + [], + [], + true, + false, + ], + [ + 'test', + false, + true, + [], + [ + $this->getUserMock('test0', 'Test'), + $this->getUserMock('test1', 'Test One'), + $this->getUserMock('test2', 'Test Two'), + ], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test0']], + ], + [ + ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ['label' => 'Test Two', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], + ], + false, + false, + ], + [ + 'test', + false, + false, + [], + [ + $this->getUserMock('test0', 'Test'), + $this->getUserMock('test1', 'Test One'), + $this->getUserMock('test2', 'Test Two'), + ], + [ + ['label' => 'Test', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test0']], + ], + [], + true, + false, + ], + [ + 'test', + true, + true, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, ['test1' => 'Test One']], + ['xyz', 'test', 2, 0, []], + ], + [], + [ + ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ], + true, + false, + ], + [ + 'test', + true, + false, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, ['test1' => 'Test One']], + ['xyz', 'test', 2, 0, []], + ], + [], + [], + true, + false, + ], + [ + 'test', + true, + true, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, [ + 'test1' => 'Test One', + 'test2' => 'Test Two', + ]], + ['xyz', 'test', 2, 0, [ + 'test1' => 'Test One', + 'test2' => 'Test Two', + ]], + ], + [], + [ + ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], + ['label' => 'Test Two', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], + ], + false, + false, + ], + [ + 'test', + true, + false, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, [ + 'test1' => 'Test One', + 'test2' => 'Test Two', + ]], + ['xyz', 'test', 2, 0, [ + 'test1' => 'Test One', + 'test2' => 'Test Two', + ]], + ], + [], + [], + true, + false, + ], + [ + 'test', + true, + true, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, [ + 'test' => 'Test One', + ]], + ['xyz', 'test', 2, 0, [ + 'test2' => 'Test Two', + ]], + ], + [ + ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], + [ + ['label' => 'Test Two', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test2']], + ], + false, + false, + ], + [ + 'test', + true, + false, + ['abc', 'xyz'], + [ + ['abc', 'test', 2, 0, [ + 'test' => 'Test One', + ]], + ['xyz', 'test', 2, 0, [ + 'test2' => 'Test Two', + ]], + ], + [ + ['label' => 'Test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test']], + ], + [], + true, + false, + ], + ]; + } + + /** + * @dataProvider dataGetUsers + * + * @param string $searchTerm + * @param bool $shareWithGroupOnly + * @param bool $shareeEnumeration + * @param array $groupResponse + * @param array $userResponse + * @param array $exactExpected + * @param array $expected + * @param bool $reachedEnd + * @param bool|IUser $singleUser + */ + public function testSearch( + $searchTerm, + $shareWithGroupOnly, + $shareeEnumeration, + array $groupResponse, + array $userResponse, + array $exactExpected, + array $expected, + $reachedEnd, + $singleUser + ) { + $this->config->expects($this->any()) + ->method('getAppValue') + ->willReturnCallback( + function($appName, $key, $default) + use ($shareWithGroupOnly, $shareeEnumeration) + { + if ($appName === 'core' && $key === 'shareapi_only_share_with_group_members') { + return $shareWithGroupOnly ? 'yes' : 'no'; + } else if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') { + return $shareeEnumeration ? 'yes' : 'no'; + } + return $default; + } + ); + + $this->instantiatePlugin(); + + $this->session->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + if(!$shareWithGroupOnly) { + $this->userManager->expects($this->once()) + ->method('searchDisplayName') + ->with($searchTerm, $this->limit, $this->offset) + ->willReturn($userResponse); + } else { + if ($singleUser !== false) { + $this->groupManager->expects($this->exactly(2)) + ->method('getUserGroupIds') + ->withConsecutive( + $this->user, + $singleUser + ) + ->willReturn($groupResponse); + } else { + $this->groupManager->expects($this->once()) + ->method('getUserGroupIds') + ->with($this->user) + ->willReturn($groupResponse); + } + + $this->groupManager->expects($this->exactly(sizeof($groupResponse))) + ->method('displayNamesInGroup') + ->with($this->anything(), $searchTerm, $this->limit, $this->offset) + ->willReturnMap($userResponse); + } + + if ($singleUser !== false) { + $this->userManager->expects($this->once()) + ->method('get') + ->with($searchTerm) + ->willReturn($singleUser); + } + + + $moreResults = $this->plugin->search($searchTerm, $this->limit, $this->offset, $this->searchResult); + $result = $this->searchResult->asArray(); + + $this->assertEquals($exactExpected, $result['exact']['users']); + $this->assertEquals($expected, $result['users']); + $this->assertSame($reachedEnd, $moreResults); + } +}