diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php index 9a97a28c376cc..3f120caefe6f1 100644 --- a/apps/user_ldap/lib/Access.php +++ b/apps/user_ldap/lib/Access.php @@ -631,7 +631,7 @@ public function mapAndAnnounceIfApplicable( * gives back the user names as they are used ownClod internally * * @param array $ldapUsers as returned by fetchList() - * @return array an array with the user names to use in Nextcloud + * @return array an array with the user names to use in Nextcloud * * gives back the user names as they are used ownClod internally * @throws \Exception @@ -644,7 +644,7 @@ public function nextcloudUserNames($ldapUsers) { * gives back the group names as they are used ownClod internally * * @param array $ldapGroups as returned by fetchList() - * @return array an array with the group names to use in Nextcloud + * @return array an array with the group names to use in Nextcloud * * gives back the group names as they are used ownClod internally * @throws \Exception @@ -655,6 +655,7 @@ public function nextcloudGroupNames($ldapGroups) { /** * @param array[] $ldapObjects as returned by fetchList() + * @return array * @throws \Exception */ private function ldap2NextcloudNames(array $ldapObjects, bool $isUsers): array { diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php index b32e031175ff9..84267171d3749 100644 --- a/apps/user_ldap/lib/Group_LDAP.php +++ b/apps/user_ldap/lib/Group_LDAP.php @@ -45,11 +45,11 @@ namespace OCA\User_LDAP; use Exception; +use OC\ServerNotAvailableException; use OCP\Cache\CappedMemoryCache; use OCP\GroupInterface; use OCP\Group\Backend\IDeleteGroupBackend; use OCP\Group\Backend\IGetDisplayNameBackend; -use OC\ServerNotAvailableException; use Psr\Log\LoggerInterface; class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, IGetDisplayNameBackend, IDeleteGroupBackend { @@ -466,7 +466,7 @@ private function prepareFilterForUsersHasGidNumber(string $groupDN, string $sear } /** - * @return array A list of users that have the given group as gid number + * @return array A list of users that have the given group as gid number * @throws ServerNotAvailableException */ public function getUsersInGidNumber( @@ -591,6 +591,7 @@ private function prepareFilterForUsersInPrimaryGroup(string $groupDN, string $se /** * @throws ServerNotAvailableException + * @return array */ public function getUsersInPrimaryGroup( string $groupDN, @@ -840,7 +841,7 @@ private function getGroupsByMember(string $dn, array &$seen = []): array { * @param string $search * @param int $limit * @param int $offset - * @return array with user ids + * @return array user ids * @throws Exception * @throws ServerNotAvailableException */ @@ -909,7 +910,11 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { if (empty($ldap_users)) { break; } - $groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]); + $uid = $this->access->dn2username($ldap_users[0]['dn'][0]); + if (!$uid) { + break; + } + $groupUsers[] = $uid; break; default: //we got DNs, check if we need to filter by search or we can give back all of them @@ -1163,7 +1168,7 @@ protected function filterValidGroups(array $listOfGroups): array { * Returns the supported actions as int to be * compared with GroupInterface::CREATE_GROUP etc. */ - public function implementsActions($actions) { + public function implementsActions($actions): bool { return (bool)((GroupInterface::COUNT_USERS | GroupInterface::DELETE_GROUP | $this->groupPluginManager->getImplementedActions()) & $actions); diff --git a/apps/user_ldap/lib/Group_Proxy.php b/apps/user_ldap/lib/Group_Proxy.php index c8c986318ec7e..5f8d0562fd918 100644 --- a/apps/user_ldap/lib/Group_Proxy.php +++ b/apps/user_ldap/lib/Group_Proxy.php @@ -171,7 +171,7 @@ public function getUserGroups($uid) { /** * get a list of all users in a group * - * @return string[] with user ids + * @return array user ids */ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { $this->setup(); @@ -334,4 +334,8 @@ public function getDisplayName(string $gid): string { public function getBackendName(): string { return 'LDAP'; } + + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array { + return $this->handleRequest($gid, 'searchInGroup', [$gid, $search, $limit, $offset]); + } } diff --git a/core/Command/User/ListCommand.php b/core/Command/User/ListCommand.php index c254a8a11cfa2..bf4bf7f030e33 100644 --- a/core/Command/User/ListCommand.php +++ b/core/Command/User/ListCommand.php @@ -74,7 +74,7 @@ protected function configure() { } protected function execute(InputInterface $input, OutputInterface $output): int { - $users = $this->userManager->search('', (int) $input->getOption('limit'), (int) $input->getOption('offset')); + $users = $this->userManager->searchDisplayName('', (int) $input->getOption('limit'), (int) $input->getOption('offset')); $this->writeArrayInOutputFormat($input, $output, $this->formatUsers($users, (bool)$input->getOption('info'))); return 0; diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index eb2f3e463546d..292e9b7b3af14 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -412,6 +412,7 @@ 'OCP\\Group\\Backend\\IIsAdminBackend' => $baseDir . '/lib/public/Group/Backend/IIsAdminBackend.php', 'OCP\\Group\\Backend\\INamedBackend' => $baseDir . '/lib/public/Group/Backend/INamedBackend.php', 'OCP\\Group\\Backend\\IRemoveFromGroupBackend' => $baseDir . '/lib/public/Group/Backend/IRemoveFromGroupBackend.php', + 'OCP\\Group\\Backend\\ISearchableGroupBackend' => $baseDir . '/lib/public/Group/Backend/ISearchableGroupBackend.php', 'OCP\\Group\\Backend\\ISetDisplayNameBackend' => $baseDir . '/lib/public/Group/Backend/ISetDisplayNameBackend.php', 'OCP\\Group\\Events\\BeforeGroupChangedEvent' => $baseDir . '/lib/public/Group/Events/BeforeGroupChangedEvent.php', 'OCP\\Group\\Events\\BeforeGroupCreatedEvent' => $baseDir . '/lib/public/Group/Events/BeforeGroupCreatedEvent.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 9343231a55b56..270d3857ca18e 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -445,6 +445,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Group\\Backend\\IIsAdminBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/IIsAdminBackend.php', 'OCP\\Group\\Backend\\INamedBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/INamedBackend.php', 'OCP\\Group\\Backend\\IRemoveFromGroupBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/IRemoveFromGroupBackend.php', + 'OCP\\Group\\Backend\\ISearchableGroupBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/ISearchableGroupBackend.php', 'OCP\\Group\\Backend\\ISetDisplayNameBackend' => __DIR__ . '/../../..' . '/lib/public/Group/Backend/ISetDisplayNameBackend.php', 'OCP\\Group\\Events\\BeforeGroupChangedEvent' => __DIR__ . '/../../..' . '/lib/public/Group/Events/BeforeGroupChangedEvent.php', 'OCP\\Group\\Events\\BeforeGroupCreatedEvent' => __DIR__ . '/../../..' . '/lib/public/Group/Events/BeforeGroupCreatedEvent.php', diff --git a/lib/private/Group/Backend.php b/lib/private/Group/Backend.php index 037cdacf4453e..1e3e5ef1164a3 100644 --- a/lib/private/Group/Backend.php +++ b/lib/private/Group/Backend.php @@ -126,7 +126,7 @@ public function groupExists($gid) { * @param string $search * @param int $limit * @param int $offset - * @return array an array of user ids + * @return array an array of user ids */ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { return []; diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php index 5f17477db772b..569cfa5007f34 100644 --- a/lib/private/Group/Database.php +++ b/lib/private/Group/Database.php @@ -40,9 +40,12 @@ use OCP\Group\Backend\IGetDisplayNameBackend; use OCP\Group\Backend\IGroupDetailsBackend; use OCP\Group\Backend\IRemoveFromGroupBackend; +use OCP\Group\Backend\ISearchableGroupBackend; use OCP\Group\Backend\ISetDisplayNameBackend; use OCP\Group\Backend\INamedBackend; use OCP\IDBConnection; +use OCP\IUserManager; +use OC\User\LazyUser; /** * Class for group management in a SQL Database (e.g. MySQL, SQLite) @@ -57,6 +60,7 @@ class Database extends ABackend implements IGroupDetailsBackend, IRemoveFromGroupBackend, ISetDisplayNameBackend, + ISearchableGroupBackend, INamedBackend { /** @var string[] */ private $groupCache = []; @@ -324,29 +328,35 @@ public function groupExists($gid) { } /** - * get a list of all users in a group + * Get a list of all users in a group * @param string $gid * @param string $search * @param int $limit * @param int $offset - * @return array an array of user ids + * @return array an array of user ids */ - public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0): array { + return array_values(array_map(fn ($user) => $user->getUid(), $this->searchInGroup($gid, $search, $limit, $offset))); + } + + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array { $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); - $query->select('g.uid') - ->from('group_user', 'g') + $query->select('g.uid', 'u.displayname'); + + $query->from('group_user', 'g') ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))) ->orderBy('g.uid', 'ASC'); + $query->leftJoin('g', 'users', 'u', $query->expr()->eq('g.uid', 'u.uid')); + if ($search !== '') { - $query->leftJoin('g', 'users', 'u', $query->expr()->eq('g.uid', 'u.uid')) - ->leftJoin('u', 'preferences', 'p', $query->expr()->andX( - $query->expr()->eq('p.userid', 'u.uid'), - $query->expr()->eq('p.appid', $query->expr()->literal('settings')), - $query->expr()->eq('p.configkey', $query->expr()->literal('email'))) - ) + $query->leftJoin('u', 'preferences', 'p', $query->expr()->andX( + $query->expr()->eq('p.userid', 'u.uid'), + $query->expr()->eq('p.appid', $query->expr()->literal('settings')), + $query->expr()->eq('p.configkey', $query->expr()->literal('email')) + )) // sqlite doesn't like re-using a single named parameter here ->andWhere( $query->expr()->orX( @@ -365,11 +375,12 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { $query->setFirstResult($offset); } - $result = $query->execute(); + $result = $query->executeQuery(); $users = []; + $userManager = \OCP\Server::get(IUserManager::class); while ($row = $result->fetch()) { - $users[] = $row['uid']; + $users[$row['uid']] = new LazyUser($row['uid'], $userManager, $row['displayname'] ?? null); } $result->closeCursor(); diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php index cca179bfe1984..efc21ad7c0dc0 100644 --- a/lib/private/Group/Group.php +++ b/lib/private/Group/Group.php @@ -33,14 +33,16 @@ namespace OC\Group; use OC\Hooks\PublicEmitter; +use OC\User\LazyUser; +use OCP\GroupInterface; use OCP\Group\Backend\ICountDisabledInGroup; use OCP\Group\Backend\IGetDisplayNameBackend; use OCP\Group\Backend\IHideFromCollaborationBackend; use OCP\Group\Backend\INamedBackend; +use OCP\Group\Backend\ISearchableGroupBackend; use OCP\Group\Backend\ISetDisplayNameBackend; use OCP\Group\Events\BeforeGroupChangedEvent; use OCP\Group\Events\GroupChangedEvent; -use OCP\GroupInterface; use OCP\IGroup; use OCP\IUser; use OCP\IUserManager; @@ -242,18 +244,23 @@ public function removeUser($user) { } /** - * search for users in the group by userid - * - * @param string $search - * @param int $limit - * @param int $offset - * @return \OC\User\User[] + * Search for users in the group by userid or display name + * @return IUser[] */ - public function searchUsers($search, $limit = null, $offset = null) { + public function searchUsers(string $search, ?int $limit = null, ?int $offset = null): array { $users = []; foreach ($this->backends as $backend) { - $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); - $users += $this->getVerifiedUsers($userIds); + if ($backend instanceof ISearchableGroupBackend) { + $users += $backend->searchInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0); + } else { + $userIds = $backend->usersInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0); + $userManager = \OCP\Server::get(IUserManager::class); + foreach ($userIds as $userId) { + if (!isset($users[$userId])) { + $users[$userId] = new LazyUser($userId, $userManager); + } + } + } if (!is_null($limit) and $limit <= 0) { return $users; } @@ -308,18 +315,11 @@ public function countDisabled() { * @param string $search * @param int $limit * @param int $offset - * @return \OC\User\User[] + * @return IUser[] + * @deprecated 27.0.0 Use searchUsers instead (same implementation) */ public function searchDisplayName($search, $limit = null, $offset = null) { - $users = []; - foreach ($this->backends as $backend) { - $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset); - $users = $this->getVerifiedUsers($userIds); - if (!is_null($limit) and $limit <= 0) { - return array_values($users); - } - } - return array_values($users); + return $this->searchUsers($search, $limit, $offset); } /** @@ -375,10 +375,7 @@ public function delete() { * @param string[] $userIds an array containing user IDs * @return \OC\User\User[] an Array with the userId as Key and \OC\User\User as value */ - private function getVerifiedUsers($userIds) { - if (!is_array($userIds)) { - return []; - } + private function getVerifiedUsers(array $userIds): array { $users = []; foreach ($userIds as $userId) { $user = $this->userManager->get($userId); diff --git a/lib/private/User/LazyUser.php b/lib/private/User/LazyUser.php index 096578b8f375d..5472cf6f2b48d 100644 --- a/lib/private/User/LazyUser.php +++ b/lib/private/User/LazyUser.php @@ -30,16 +30,26 @@ class LazyUser implements IUser { private ?IUser $user = null; private string $uid; + private ?string $displayName; private IUserManager $userManager; + private ?UserInterface $backend; - public function __construct(string $uid, IUserManager $userManager) { + public function __construct(string $uid, IUserManager $userManager, ?string $displayName = null, ?UserInterface $backend = null) { $this->uid = $uid; $this->userManager = $userManager; + $this->displayName = $displayName; + $this->backend = $backend; } private function getUser(): IUser { if ($this->user === null) { - $this->user = $this->userManager->get($this->uid); + if ($this->backend) { + /** @var \OC\User\Manager $manager */ + $manager = $this->userManager; + $this->user = $manager->getUserObject($this->uid, $this->backend); + } else { + $this->user = $this->userManager->get($this->uid); + } } /** @var IUser */ $user = $this->user; @@ -51,6 +61,10 @@ public function getUID() { } public function getDisplayName() { + if ($this->displayName) { + return $this->displayName; + } + return $this->userManager->getDisplayName($this->uid) ?? $this->uid; } diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index 859ebd2a604a8..60059d5baddbe 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -202,7 +202,7 @@ public function getDisplayName(string $uid): ?string { * @param bool $cacheUser If false the newly created user object will not be cached * @return \OC\User\User */ - protected function getUserObject($uid, $backend, $cacheUser = true) { + public function getUserObject($uid, $backend, $cacheUser = true) { if ($backend instanceof IGetRealUIDBackend) { $uid = $backend->getRealUID($uid); } @@ -293,12 +293,13 @@ public function checkPasswordNoLogging($loginName, $password) { } /** - * search by user id + * Search by user id * * @param string $pattern * @param int $limit * @param int $offset - * @return \OC\User\User[] + * @return IUser[] + * @deprecated since 27.0.0, use searchDisplayName instead */ public function search($pattern, $limit = null, $offset = null) { $users = []; @@ -306,28 +307,24 @@ public function search($pattern, $limit = null, $offset = null) { $backendUsers = $backend->getUsers($pattern, $limit, $offset); if (is_array($backendUsers)) { foreach ($backendUsers as $uid) { - $users[$uid] = $this->getUserObject($uid, $backend); + $users[$uid] = new LazyUser($uid, $this, null, $backend); } } } - uasort($users, function ($a, $b) { - /** - * @var \OC\User\User $a - * @var \OC\User\User $b - */ + uasort($users, function (IUser $a, IUser $b) { return strcasecmp($a->getUID(), $b->getUID()); }); return $users; } /** - * search by displayName + * Search by displayName * * @param string $pattern * @param int $limit * @param int $offset - * @return \OC\User\User[] + * @return IUser[] */ public function searchDisplayName($pattern, $limit = null, $offset = null) { $users = []; @@ -335,16 +332,12 @@ public function searchDisplayName($pattern, $limit = null, $offset = null) { $backendUsers = $backend->getDisplayNames($pattern, $limit, $offset); if (is_array($backendUsers)) { foreach ($backendUsers as $uid => $displayName) { - $users[] = $this->getUserObject($uid, $backend); + $users[] = new LazyUser($uid, $this, $displayName, $backend); } } } - usort($users, function ($a, $b) { - /** - * @var \OC\User\User $a - * @var \OC\User\User $b - */ + usort($users, function (IUser $a, IUser $b) { return strcasecmp($a->getDisplayName(), $b->getDisplayName()); }); return $users; diff --git a/lib/public/Group/Backend/ISearchableGroupBackend.php b/lib/public/Group/Backend/ISearchableGroupBackend.php new file mode 100644 index 0000000000000..20753822258e4 --- /dev/null +++ b/lib/public/Group/Backend/ISearchableGroupBackend.php @@ -0,0 +1,51 @@ + + * + * @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\Group\Backend; + +use OCP\IUser; + +/** + * @since 27.0.0 + */ +interface ISearchableGroupBackend { + /** + * @brief Get a list of users matching the given search parameters. + * + * Implementations of this method should return lazy evaluated user objects and + * preload if possible the display name. + * + * + * $users = $groupBackend->searchInGroup('admin', 'John', 10, 0); + * + * + * @param string $gid The group id of the user we want to search + * @param string $search The part of the display name or user id of the users we + * want to search. This can be empty to get all the users. + * @param int $limit The limit of results + * @param int $offset The offset of the results + * @return array Users indexed by uid + * @since 27.0.0 + */ + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array; +} diff --git a/lib/public/GroupInterface.php b/lib/public/GroupInterface.php index b7c0713612612..56863100c0504 100644 --- a/lib/public/GroupInterface.php +++ b/lib/public/GroupInterface.php @@ -106,13 +106,15 @@ public function getGroups($search = '', $limit = -1, $offset = 0); public function groupExists($gid); /** - * get a list of all users in a group + * @brief Get a list of user ids in a group matching the given search parameters. + * * @param string $gid * @param string $search * @param int $limit * @param int $offset - * @return array an array of user ids + * @return array an array of user ids * @since 4.5.0 + * @deprecated 27.0.0 Use searchInGroup instead, for performance reasons */ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0); } diff --git a/lib/public/IGroup.php b/lib/public/IGroup.php index ba13359c472d9..ec26cc55b69ac 100644 --- a/lib/public/IGroup.php +++ b/lib/public/IGroup.php @@ -99,7 +99,7 @@ public function removeUser($user); * @return \OCP\IUser[] * @since 8.0.0 */ - public function searchUsers($search, $limit = null, $offset = null); + public function searchUsers(string $search, ?int $limit = null, ?int $offset = null): array; /** * returns the number of users matching the search string diff --git a/tests/lib/Group/GroupTest.php b/tests/lib/Group/GroupTest.php index 60f15a6573206..ac648fd7b4bbf 100644 --- a/tests/lib/Group/GroupTest.php +++ b/tests/lib/Group/GroupTest.php @@ -303,14 +303,14 @@ public function testSearchUsers() { $group = new \OC\Group\Group('group1', [$backend], $this->dispatcher, $userManager); $backend->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('group1', '2') - ->willReturn(['user2']); + ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]); $users = $group->searchUsers('2'); $this->assertEquals(1, count($users)); - $user2 = $users['user2']; + $user2 = reset($users); $this->assertEquals('user2', $user2->getUID()); } @@ -325,18 +325,18 @@ public function testSearchUsersMultipleBackends() { $group = new \OC\Group\Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager); $backend1->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('group1', '2') - ->willReturn(['user2']); + ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]); $backend2->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('group1', '2') - ->willReturn(['user2']); + ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]); $users = $group->searchUsers('2'); $this->assertEquals(1, count($users)); - $user2 = $users['user2']; + $user2 = reset($users); $this->assertEquals('user2', $user2->getUID()); } @@ -348,14 +348,14 @@ public function testSearchUsersLimitAndOffset() { $group = new \OC\Group\Group('group1', [$backend], $this->dispatcher, $userManager); $backend->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('group1', 'user', 1, 1) - ->willReturn(['user2']); + ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]); $users = $group->searchUsers('user', 1, 1); $this->assertEquals(1, count($users)); - $user2 = $users['user2']; + $user2 = reset($users); $this->assertEquals('user2', $user2->getUID()); } @@ -370,19 +370,19 @@ public function testSearchUsersMultipleBackendsLimitAndOffset() { $group = new \OC\Group\Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager); $backend1->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('group1', 'user', 2, 1) - ->willReturn(['user2']); + ->willReturn(['user2' => new \OC\User\User('user2', null, $this->dispatcher)]); $backend2->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('group1', 'user', 2, 1) - ->willReturn(['user1']); + ->willReturn(['user1' => new \OC\User\User('user1', null, $this->dispatcher)]); $users = $group->searchUsers('user', 2, 1); $this->assertEquals(2, count($users)); - $user2 = $users['user2']; - $user1 = $users['user1']; + $user2 = reset($users); + $user1 = next($users); $this->assertEquals('user2', $user2->getUID()); $this->assertEquals('user1', $user1->getUID()); } diff --git a/tests/lib/Group/ManagerTest.php b/tests/lib/Group/ManagerTest.php index 5e29f974706b7..710d3888d55ab 100644 --- a/tests/lib/Group/ManagerTest.php +++ b/tests/lib/Group/ManagerTest.php @@ -24,8 +24,10 @@ namespace Test\Group; use OC\Group\Database; +use OC\User\User; use OC\User\Manager; use OCP\GroupInterface; +use OCP\Group\Backend\ISearchableGroupBackend; use OCP\ICacheFactory; use OCP\IUser; use PHPUnit\Framework\MockObject\MockObject; @@ -33,6 +35,9 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Test\TestCase; +interface ISearchableGroupInterface extends ISearchableGroupBackend, GroupInterface { +} + class ManagerTest extends TestCase { /** @var Manager|MockObject */ protected $userManager; @@ -78,7 +83,7 @@ private function getTestBackend($implementedActions = null) { } // need to declare it this way due to optional methods // thanks to the implementsActions logic - $backend = $this->getMockBuilder(GroupInterface::class) + $backend = $this->getMockBuilder(ISearchableGroupInterface::class) ->disableOriginalConstructor() ->setMethods([ 'getGroupDetails', @@ -91,6 +96,7 @@ private function getTestBackend($implementedActions = null) { 'createGroup', 'addToGroup', 'removeFromGroup', + 'searchInGroup', ]) ->getMock(); $backend->expects($this->any()) @@ -724,7 +730,7 @@ public function testDisplayNamesInGroupWithOneUserBackendWithLimitAndOffsetSpeci public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmpty() { /** - * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend + * @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend */ $backend = $this->getTestBackend(); $backend->expects($this->exactly(1)) @@ -733,22 +739,11 @@ public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmpty() { ->willReturn(true); $backend->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('testgroup', '', -1, 0) - ->willReturn(['user2', 'user33']); + ->willReturn(['user2' => $this->getTestUser('user2'), 'user33' => $this->getTestUser('user33')]); - $this->userManager->expects($this->any()) - ->method('get') - ->willReturnCallback(function ($uid) { - switch ($uid) { - case 'user1': return $this->getTestUser('user1'); - case 'user2': return $this->getTestUser('user2'); - case 'user3': return $this->getTestUser('user3'); - case 'user33': return $this->getTestUser('user33'); - default: - return null; - } - }); + $this->userManager->expects($this->never())->method('get'); $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache); $manager->addBackend($backend); @@ -772,22 +767,11 @@ public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmptyAndLimitS ->willReturn(true); $backend->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('testgroup', '', 1, 0) - ->willReturn(['user2']); + ->willReturn([new User('user2', null, $this->dispatcher)]); - $this->userManager->expects($this->any()) - ->method('get') - ->willReturnCallback(function ($uid) { - switch ($uid) { - case 'user1': return $this->getTestUser('user1'); - case 'user2': return $this->getTestUser('user2'); - case 'user3': return $this->getTestUser('user3'); - case 'user33': return $this->getTestUser('user33'); - default: - return null; - } - }); + $this->userManager->expects($this->never())->method('get'); $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache); $manager->addBackend($backend); @@ -811,22 +795,11 @@ public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmptyAndLimitA ->willReturn(true); $backend->expects($this->once()) - ->method('usersInGroup') + ->method('searchInGroup') ->with('testgroup', '', 1, 1) - ->willReturn(['user33']); + ->willReturn(['user33' => $this->getTestUser('user33')]); - $this->userManager->expects($this->any()) - ->method('get') - ->willReturnCallback(function ($uid) { - switch ($uid) { - case 'user1': return $this->getTestUser('user1'); - case 'user2': return $this->getTestUser('user2'); - case 'user3': return $this->getTestUser('user3'); - case 'user33': return $this->getTestUser('user33'); - default: - return null; - } - }); + $this->userManager->expects($this->never())->method('get'); $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache); $manager->addBackend($backend); diff --git a/tests/lib/Util/Group/Dummy.php b/tests/lib/Util/Group/Dummy.php index 3735a5e1167e8..a864c8ce9d992 100644 --- a/tests/lib/Util/Group/Dummy.php +++ b/tests/lib/Util/Group/Dummy.php @@ -29,12 +29,18 @@ namespace Test\Util\Group; -use OC\Group\Backend; +use Test\Util\User\Dummy as DummyUser; +use OCP\Group\Backend\ABackend; +use OCP\Group\Backend\IDeleteGroupBackend; +use OCP\Group\Backend\IAddToGroupBackend; +use OCP\Group\Backend\IRemoveFromGroupBackend; +use OCP\Group\Backend\ICreateGroupBackend; +use OCP\Group\Backend\ICountUsersBackend; /** - * dummy group backend, does not keep state, only for testing use + * Dummy group backend, does not keep state, only for testing use */ -class Dummy extends Backend { +class Dummy extends ABackend implements ICreateGroupBackend, IDeleteGroupBackend, IAddToGroupBackend, IRemoveFromGroupBackend, ICountUsersBackend { private $groups = []; /** * Try to create a new group @@ -44,7 +50,7 @@ class Dummy extends Backend { * Tries to create a new group. If the group name already exists, false will * be returned. */ - public function createGroup($gid) { + public function createGroup(string $gid): bool { if (!isset($this->groups[$gid])) { $this->groups[$gid] = []; return true; @@ -60,7 +66,7 @@ public function createGroup($gid) { * * Deletes a group and removes it from the group_user-table */ - public function deleteGroup($gid) { + public function deleteGroup(string $gid): bool { if (isset($this->groups[$gid])) { unset($this->groups[$gid]); return true; @@ -93,7 +99,7 @@ public function inGroup($uid, $gid) { * * Adds a user to a group. */ - public function addToGroup($uid, $gid) { + public function addToGroup(string $uid, string $gid): bool { if (isset($this->groups[$gid])) { if (array_search($uid, $this->groups[$gid]) === false) { $this->groups[$gid][] = $uid; @@ -114,7 +120,7 @@ public function addToGroup($uid, $gid) { * * removes the user from a group. */ - public function removeFromGroup($uid, $gid) { + public function removeFromGroup(string $uid, string $gid): bool { if (isset($this->groups[$gid])) { if (($index = array_search($uid, $this->groups[$gid])) !== false) { unset($this->groups[$gid][$index]); @@ -192,6 +198,25 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { } } + public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array { + if (isset($this->groups[$gid])) { + if (empty($search)) { + $length = $limit < 0 ? null : $limit; + $users = array_slice($this->groups[$gid], $offset, $length); + return array_map(fn ($user) => new DummyUser($user, '')); + } + $result = []; + foreach ($this->groups[$gid] as $user) { + if (stripos($user, $search) !== false) { + $result[$user] = new DummyUser($user, ''); + } + } + return $result; + } else { + return []; + } + } + /** * get the number of all users in a group * @param string $gid @@ -200,7 +225,7 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { * @param int $offset * @return int */ - public function countUsersInGroup($gid, $search = '', $limit = -1, $offset = 0) { + public function countUsersInGroup(string $gid, string $search = ''): int { if (isset($this->groups[$gid])) { if (empty($search)) { return count($this->groups[$gid]); @@ -213,5 +238,10 @@ public function countUsersInGroup($gid, $search = '', $limit = -1, $offset = 0) } return $count; } + return 0; + } + + public function groupExists($gid) { + return isset($this->groups[$gid]); } }