aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Group
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Group')
-rw-r--r--lib/private/Group/Backend.php29
-rw-r--r--lib/private/Group/Database.php252
-rw-r--r--lib/private/Group/DisplayNameCache.php76
-rw-r--r--lib/private/Group/Group.php162
-rw-r--r--lib/private/Group/Manager.php237
-rw-r--r--lib/private/Group/MetaData.php59
6 files changed, 465 insertions, 350 deletions
diff --git a/lib/private/Group/Backend.php b/lib/private/Group/Backend.php
index 037cdacf445..f4a90018b5a 100644
--- a/lib/private/Group/Backend.php
+++ b/lib/private/Group/Backend.php
@@ -1,26 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Knut Ahlers <knut@ahlers.me>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Group;
@@ -88,7 +71,7 @@ abstract class Backend implements \OCP\GroupInterface {
/**
* Get all groups a user belongs to
* @param string $uid Name of the user
- * @return array an array of group names
+ * @return list<string> an array of group names
*
* This function fetches all groups a user belongs to. It does not check
* if the user exists at all.
@@ -126,7 +109,7 @@ abstract class Backend implements \OCP\GroupInterface {
* @param string $search
* @param int $limit
* @param int $offset
- * @return array an array of user ids
+ * @return array<int,string> 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 7b7ee41def9..0cb571a3935 100644
--- a/lib/private/Group/Database.php
+++ b/lib/private/Group/Database.php
@@ -1,77 +1,58 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Johannes Leuker <j.leuker@hosting.de>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Loki3000 <github@labcms.ru>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author tgrant <tom.grant760@gmail.com>
- * @author Tom Grant <TomG736@users.noreply.github.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Group;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
+use OC\User\LazyUser;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Group\Backend\ABackend;
use OCP\Group\Backend\IAddToGroupBackend;
+use OCP\Group\Backend\IBatchMethodsBackend;
use OCP\Group\Backend\ICountDisabledInGroup;
use OCP\Group\Backend\ICountUsersBackend;
-use OCP\Group\Backend\ICreateGroupBackend;
+use OCP\Group\Backend\ICreateNamedGroupBackend;
use OCP\Group\Backend\IDeleteGroupBackend;
use OCP\Group\Backend\IGetDisplayNameBackend;
use OCP\Group\Backend\IGroupDetailsBackend;
+use OCP\Group\Backend\INamedBackend;
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;
/**
* Class for group management in a SQL Database (e.g. MySQL, SQLite)
*/
class Database extends ABackend implements
IAddToGroupBackend,
- ICountDisabledInGroup,
- ICountUsersBackend,
- ICreateGroupBackend,
- IDeleteGroupBackend,
- IGetDisplayNameBackend,
- IGroupDetailsBackend,
- IRemoveFromGroupBackend,
- ISetDisplayNameBackend,
- INamedBackend {
-
- /** @var string[] */
+ ICountDisabledInGroup,
+ ICountUsersBackend,
+ ICreateNamedGroupBackend,
+ IDeleteGroupBackend,
+ IGetDisplayNameBackend,
+ IGroupDetailsBackend,
+ IRemoveFromGroupBackend,
+ ISetDisplayNameBackend,
+ ISearchableGroupBackend,
+ IBatchMethodsBackend,
+ INamedBackend {
+ /** @var array<string, array{gid: string, displayname: string}> */
private $groupCache = [];
- /** @var IDBConnection */
- private $dbConn;
-
/**
* \OC\Group\Database constructor.
*
* @param IDBConnection|null $dbConn
*/
- public function __construct(IDBConnection $dbConn = null) {
- $this->dbConn = $dbConn;
+ public function __construct(
+ private ?IDBConnection $dbConn = null,
+ ) {
}
/**
@@ -83,35 +64,28 @@ class Database extends ABackend implements
}
}
- /**
- * Try to create a new group
- * @param string $gid The name of the group to create
- * @return bool
- *
- * Tries to create a new group. If the group name already exists, false will
- * be returned.
- */
- public function createGroup(string $gid): bool {
+ public function createGroup(string $name): ?string {
$this->fixDI();
+ $gid = $this->computeGid($name);
try {
// Add group
$builder = $this->dbConn->getQueryBuilder();
$result = $builder->insert('groups')
->setValue('gid', $builder->createNamedParameter($gid))
- ->setValue('displayname', $builder->createNamedParameter($gid))
+ ->setValue('displayname', $builder->createNamedParameter($name))
->execute();
} catch (UniqueConstraintViolationException $e) {
- $result = 0;
+ return null;
}
// Add to cache
$this->groupCache[$gid] = [
'gid' => $gid,
- 'displayname' => $gid
+ 'displayname' => $name
];
- return $result === 1;
+ return $gid;
}
/**
@@ -128,19 +102,19 @@ class Database extends ABackend implements
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('groups')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
- ->execute();
+ ->executeStatement();
// Delete the group-user relation
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('group_user')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
- ->execute();
+ ->executeStatement();
// Delete the group-groupadmin relation
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('group_admin')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
- ->execute();
+ ->executeStatement();
// Delete from cache
unset($this->groupCache[$gid]);
@@ -165,7 +139,7 @@ class Database extends ABackend implements
->from('group_user')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
- ->execute();
+ ->executeQuery();
$result = $cursor->fetch();
$cursor->closeCursor();
@@ -190,7 +164,7 @@ class Database extends ABackend implements
$qb->insert('group_user')
->setValue('uid', $qb->createNamedParameter($uid))
->setValue('gid', $qb->createNamedParameter($gid))
- ->execute();
+ ->executeStatement();
return true;
} else {
return false;
@@ -212,7 +186,7 @@ class Database extends ABackend implements
$qb->delete('group_user')
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
->andWhere($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
- ->execute();
+ ->executeStatement();
return true;
}
@@ -220,7 +194,7 @@ class Database extends ABackend implements
/**
* Get all groups a user belongs to
* @param string $uid Name of the user
- * @return array an array of group names
+ * @return list<string> an array of group names
*
* This function fetches all groups a user belongs to. It does not check
* if the user exists at all.
@@ -239,7 +213,7 @@ class Database extends ABackend implements
->from('group_user', 'gu')
->leftJoin('gu', 'groups', 'g', $qb->expr()->eq('gu.gid', 'g.gid'))
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
- ->execute();
+ ->executeQuery();
$groups = [];
while ($row = $cursor->fetch()) {
@@ -263,11 +237,11 @@ class Database extends ABackend implements
*
* Returns a list with all groups
*/
- public function getGroups($search = '', $limit = null, $offset = null) {
+ public function getGroups(string $search = '', int $limit = -1, int $offset = 0) {
$this->fixDI();
$query = $this->dbConn->getQueryBuilder();
- $query->select('gid')
+ $query->select('gid', 'displayname')
->from('groups')
->orderBy('gid', 'ASC');
@@ -280,12 +254,20 @@ class Database extends ABackend implements
)));
}
- $query->setMaxResults($limit)
- ->setFirstResult($offset);
- $result = $query->execute();
+ if ($limit > 0) {
+ $query->setMaxResults($limit);
+ }
+ if ($offset > 0) {
+ $query->setFirstResult($offset);
+ }
+ $result = $query->executeQuery();
$groups = [];
while ($row = $result->fetch()) {
+ $this->groupCache[$row['gid']] = [
+ 'displayname' => $row['displayname'],
+ 'gid' => $row['gid'],
+ ];
$groups[] = $row['gid'];
}
$result->closeCursor();
@@ -310,7 +292,7 @@ class Database extends ABackend implements
$cursor = $qb->select('gid', 'displayname')
->from('groups')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
- ->execute();
+ ->executeQuery();
$result = $cursor->fetch();
$cursor->closeCursor();
@@ -325,29 +307,72 @@ class Database extends ABackend implements
}
/**
- * get a list of all users in a group
+ * {@inheritdoc}
+ */
+ public function groupsExists(array $gids): array {
+ $notFoundGids = [];
+ $existingGroups = [];
+
+ // In case the data is already locally accessible, not need to do SQL query
+ // or do a SQL query but with a smaller in clause
+ foreach ($gids as $gid) {
+ if (isset($this->groupCache[$gid])) {
+ $existingGroups[] = $gid;
+ } else {
+ $notFoundGids[] = $gid;
+ }
+ }
+
+ $qb = $this->dbConn->getQueryBuilder();
+ $qb->select('gid', 'displayname')
+ ->from('groups')
+ ->where($qb->expr()->in('gid', $qb->createParameter('ids')));
+ foreach (array_chunk($notFoundGids, 1000) as $chunk) {
+ $qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
+ $result = $qb->executeQuery();
+ while ($row = $result->fetch()) {
+ $this->groupCache[(string)$row['gid']] = [
+ 'displayname' => (string)$row['displayname'],
+ 'gid' => (string)$row['gid'],
+ ];
+ $existingGroups[] = (string)$row['gid'];
+ }
+ $result->closeCursor();
+ }
+
+ return $existingGroups;
+ }
+
+ /**
+ * 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<int,string> 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(
@@ -366,11 +391,12 @@ class Database extends ABackend implements
$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();
@@ -397,7 +423,7 @@ class Database extends ABackend implements
)));
}
- $result = $query->execute();
+ $result = $query->executeQuery();
$count = $result->fetchOne();
$result->closeCursor();
@@ -429,7 +455,7 @@ class Database extends ABackend implements
->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
->andWhere($query->expr()->eq('gid', $query->createNamedParameter($gid), IQueryBuilder::PARAM_STR));
- $result = $query->execute();
+ $result = $query->executeQuery();
$count = $result->fetchOne();
$result->closeCursor();
@@ -458,11 +484,11 @@ class Database extends ABackend implements
->from('groups')
->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
- $result = $query->execute();
+ $result = $query->executeQuery();
$displayName = $result->fetchOne();
$result->closeCursor();
- return (string) $displayName;
+ return (string)$displayName;
}
public function getGroupDetails(string $gid): array {
@@ -474,6 +500,45 @@ class Database extends ABackend implements
return [];
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getGroupsDetails(array $gids): array {
+ $notFoundGids = [];
+ $details = [];
+
+ $this->fixDI();
+
+ // In case the data is already locally accessible, not need to do SQL query
+ // or do a SQL query but with a smaller in clause
+ foreach ($gids as $gid) {
+ if (isset($this->groupCache[$gid])) {
+ $details[$gid] = ['displayName' => $this->groupCache[$gid]['displayname']];
+ } else {
+ $notFoundGids[] = $gid;
+ }
+ }
+
+ foreach (array_chunk($notFoundGids, 1000) as $chunk) {
+ $query = $this->dbConn->getQueryBuilder();
+ $query->select('gid', 'displayname')
+ ->from('groups')
+ ->where($query->expr()->in('gid', $query->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY)));
+
+ $result = $query->executeQuery();
+ while ($row = $result->fetch()) {
+ $details[(string)$row['gid']] = ['displayName' => (string)$row['displayname']];
+ $this->groupCache[(string)$row['gid']] = [
+ 'displayname' => (string)$row['displayname'],
+ 'gid' => (string)$row['gid'],
+ ];
+ }
+ $result->closeCursor();
+ }
+
+ return $details;
+ }
+
public function setDisplayName(string $gid, string $displayName): bool {
if (!$this->groupExists($gid)) {
return false;
@@ -490,7 +555,7 @@ class Database extends ABackend implements
$query->update('groups')
->set('displayname', $query->createNamedParameter($displayName))
->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
- $query->execute();
+ $query->executeStatement();
return true;
}
@@ -503,4 +568,13 @@ class Database extends ABackend implements
public function getBackendName(): string {
return 'Database';
}
+
+ /**
+ * Compute group ID from display name (GIDs are limited to 64 characters in database)
+ */
+ private function computeGid(string $displayName): string {
+ return mb_strlen($displayName) > 64
+ ? hash('sha256', $displayName)
+ : $displayName;
+ }
}
diff --git a/lib/private/Group/DisplayNameCache.php b/lib/private/Group/DisplayNameCache.php
new file mode 100644
index 00000000000..fef8d40a05f
--- /dev/null
+++ b/lib/private/Group/DisplayNameCache.php
@@ -0,0 +1,76 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Group;
+
+use OCP\Cache\CappedMemoryCache;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Group\Events\GroupChangedEvent;
+use OCP\Group\Events\GroupDeletedEvent;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IGroupManager;
+
+/**
+ * Class that cache the relation Group ID -> Display name
+ *
+ * This saves fetching the group from the backend for "just" the display name
+ * @template-implements IEventListener<GroupChangedEvent|GroupDeletedEvent>
+ */
+class DisplayNameCache implements IEventListener {
+ private CappedMemoryCache $cache;
+ private ICache $memCache;
+ private IGroupManager $groupManager;
+
+ public function __construct(ICacheFactory $cacheFactory, IGroupManager $groupManager) {
+ $this->cache = new CappedMemoryCache();
+ $this->memCache = $cacheFactory->createDistributed('groupDisplayNameMappingCache');
+ $this->groupManager = $groupManager;
+ }
+
+ public function getDisplayName(string $groupId): ?string {
+ if (isset($this->cache[$groupId])) {
+ return $this->cache[$groupId];
+ }
+ $displayName = $this->memCache->get($groupId);
+ if ($displayName) {
+ $this->cache[$groupId] = $displayName;
+ return $displayName;
+ }
+
+ $group = $this->groupManager->get($groupId);
+ if ($group) {
+ $displayName = $group->getDisplayName();
+ } else {
+ $displayName = null;
+ }
+ $this->cache[$groupId] = $displayName;
+ $this->memCache->set($groupId, $displayName, 60 * 10); // 10 minutes
+
+ return $displayName;
+ }
+
+ public function clear(): void {
+ $this->cache = new CappedMemoryCache();
+ $this->memCache->clear();
+ }
+
+ public function handle(Event $event): void {
+ if ($event instanceof GroupChangedEvent && $event->getFeature() === 'displayName') {
+ $groupId = $event->getGroup()->getGID();
+ $newDisplayName = $event->getValue();
+ $this->cache[$groupId] = $newDisplayName;
+ $this->memCache->set($groupId, $newDisplayName, 60 * 10); // 10 minutes
+ }
+ if ($event instanceof GroupDeletedEvent) {
+ $groupId = $event->getGroup()->getGID();
+ unset($this->cache[$groupId]);
+ $this->memCache->remove($groupId);
+ }
+ }
+}
diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php
index 2ef4d2ee23f..6e42fad8b9f 100644
--- a/lib/private/Group/Group.php
+++ b/lib/private/Group/Group.php
@@ -1,52 +1,36 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bart Visscher <bartv@thisnet.nl>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Johannes Leuker <j.leuker@hosting.de>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Robin McCorkell <robin@mccorkell.me.uk>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Group;
use OC\Hooks\PublicEmitter;
+use OC\User\LazyUser;
+use OCP\EventDispatcher\IEventDispatcher;
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\BeforeGroupDeletedEvent;
+use OCP\Group\Events\BeforeUserAddedEvent;
+use OCP\Group\Events\BeforeUserRemovedEvent;
+use OCP\Group\Events\GroupChangedEvent;
+use OCP\Group\Events\GroupDeletedEvent;
+use OCP\Group\Events\UserAddedEvent;
+use OCP\Group\Events\UserRemovedEvent;
use OCP\GroupInterface;
use OCP\IGroup;
use OCP\IUser;
use OCP\IUserManager;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\EventDispatcher\GenericEvent;
class Group implements IGroup {
- /** @var null|string */
+ /** @var null|string */
protected $displayName;
/** @var string */
@@ -60,23 +44,14 @@ class Group implements IGroup {
/** @var Backend[] */
private $backends;
- /** @var EventDispatcherInterface */
+ /** @var IEventDispatcher */
private $dispatcher;
- /** @var \OC\User\Manager|IUserManager */
+ /** @var \OC\User\Manager|IUserManager */
private $userManager;
/** @var PublicEmitter */
private $emitter;
-
- /**
- * @param string $gid
- * @param Backend[] $backends
- * @param EventDispatcherInterface $dispatcher
- * @param IUserManager $userManager
- * @param PublicEmitter $emitter
- * @param string $displayName
- */
- public function __construct(string $gid, array $backends, EventDispatcherInterface $dispatcher, IUserManager $userManager, PublicEmitter $emitter = null, ?string $displayName = null) {
+ public function __construct(string $gid, array $backends, IEventDispatcher $dispatcher, IUserManager $userManager, ?PublicEmitter $emitter = null, ?string $displayName = null) {
$this->gid = $gid;
$this->backends = $backends;
$this->dispatcher = $dispatcher;
@@ -85,11 +60,11 @@ class Group implements IGroup {
$this->displayName = $displayName;
}
- public function getGID() {
+ public function getGID(): string {
return $this->gid;
}
- public function getDisplayName() {
+ public function getDisplayName(): string {
if (is_null($this->displayName)) {
foreach ($this->backends as $backend) {
if ($backend instanceof IGetDisplayNameBackend) {
@@ -108,10 +83,12 @@ class Group implements IGroup {
public function setDisplayName(string $displayName): bool {
$displayName = trim($displayName);
if ($displayName !== '') {
+ $this->dispatcher->dispatchTyped(new BeforeGroupChangedEvent($this, 'displayName', $displayName, $this->displayName));
foreach ($this->backends as $backend) {
if (($backend instanceof ISetDisplayNameBackend)
&& $backend->setDisplayName($this->gid, $displayName)) {
$this->displayName = $displayName;
+ $this->dispatcher->dispatchTyped(new GroupChangedEvent($this, 'displayName', $displayName, ''));
return true;
}
}
@@ -124,7 +101,7 @@ class Group implements IGroup {
*
* @return \OC\User\User[]
*/
- public function getUsers() {
+ public function getUsers(): array {
if ($this->usersLoaded) {
return $this->users;
}
@@ -151,7 +128,7 @@ class Group implements IGroup {
* @param IUser $user
* @return bool
*/
- public function inGroup(IUser $user) {
+ public function inGroup(IUser $user): bool {
if (isset($this->users[$user->getUID()])) {
return true;
}
@@ -169,14 +146,12 @@ class Group implements IGroup {
*
* @param IUser $user
*/
- public function addUser(IUser $user) {
+ public function addUser(IUser $user): void {
if ($this->inGroup($user)) {
return;
}
- $this->dispatcher->dispatch(IGroup::class . '::preAddUser', new GenericEvent($this, [
- 'user' => $user,
- ]));
+ $this->dispatcher->dispatchTyped(new BeforeUserAddedEvent($this, $user));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'preAddUser', [$this, $user]);
@@ -184,13 +159,9 @@ class Group implements IGroup {
foreach ($this->backends as $backend) {
if ($backend->implementsActions(\OC\Group\Backend::ADD_TO_GROUP)) {
$backend->addToGroup($user->getUID(), $this->gid);
- if ($this->users) {
- $this->users[$user->getUID()] = $user;
- }
+ $this->users[$user->getUID()] = $user;
- $this->dispatcher->dispatch(IGroup::class . '::postAddUser', new GenericEvent($this, [
- 'user' => $user,
- ]));
+ $this->dispatcher->dispatchTyped(new UserAddedEvent($this, $user));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'postAddUser', [$this, $user]);
@@ -202,14 +173,10 @@ class Group implements IGroup {
/**
* remove a user from the group
- *
- * @param \OC\User\User $user
*/
- public function removeUser($user) {
+ public function removeUser(IUser $user): void {
$result = false;
- $this->dispatcher->dispatch(IGroup::class . '::preRemoveUser', new GenericEvent($this, [
- 'user' => $user,
- ]));
+ $this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($this, $user));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'preRemoveUser', [$this, $user]);
}
@@ -220,9 +187,7 @@ class Group implements IGroup {
}
}
if ($result) {
- $this->dispatcher->dispatch(IGroup::class . '::postRemoveUser', new GenericEvent($this, [
- 'user' => $user,
- ]));
+ $this->dispatcher->dispatchTyped(new UserRemovedEvent($this, $user));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'postRemoveUser', [$this, $user]);
}
@@ -238,18 +203,23 @@ class Group implements IGroup {
}
/**
- * 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;
}
@@ -263,7 +233,7 @@ class Group implements IGroup {
* @param string $search
* @return int|bool
*/
- public function count($search = '') {
+ public function count($search = ''): int|bool {
$users = false;
foreach ($this->backends as $backend) {
if ($backend->implementsActions(\OC\Group\Backend::COUNT_USERS)) {
@@ -283,7 +253,7 @@ class Group implements IGroup {
*
* @return int|bool
*/
- public function countDisabled() {
+ public function countDisabled(): int|bool {
$users = false;
foreach ($this->backends as $backend) {
if ($backend instanceof ICountDisabledInGroup) {
@@ -304,18 +274,11 @@ class Group implements IGroup {
* @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);
+ public function searchDisplayName(string $search, ?int $limit = null, ?int $offset = null): array {
+ return $this->searchUsers($search, $limit, $offset);
}
/**
@@ -323,7 +286,7 @@ class Group implements IGroup {
*
* @return string[]
*/
- public function getBackendNames() {
+ public function getBackendNames(): array {
$backends = [];
foreach ($this->backends as $backend) {
if ($backend instanceof INamedBackend) {
@@ -337,18 +300,18 @@ class Group implements IGroup {
}
/**
- * delete the group
+ * Delete the group
*
* @return bool
*/
- public function delete() {
+ public function delete(): bool {
// Prevent users from deleting group admin
if ($this->getGID() === 'admin') {
return false;
}
$result = false;
- $this->dispatcher->dispatch(IGroup::class . '::preDelete', new GenericEvent($this));
+ $this->dispatcher->dispatchTyped(new BeforeGroupDeletedEvent($this));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'preDelete', [$this]);
}
@@ -358,7 +321,7 @@ class Group implements IGroup {
}
}
if ($result) {
- $this->dispatcher->dispatch(IGroup::class . '::postDelete', new GenericEvent($this));
+ $this->dispatcher->dispatchTyped(new GroupDeletedEvent($this));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'postDelete', [$this]);
}
@@ -371,10 +334,7 @@ class Group implements IGroup {
* @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);
@@ -389,7 +349,7 @@ class Group implements IGroup {
* @return bool
* @since 14.0.0
*/
- public function canRemoveUser() {
+ public function canRemoveUser(): bool {
foreach ($this->backends as $backend) {
if ($backend->implementsActions(GroupInterface::REMOVE_FROM_GOUP)) {
return true;
@@ -402,7 +362,7 @@ class Group implements IGroup {
* @return bool
* @since 14.0.0
*/
- public function canAddUser() {
+ public function canAddUser(): bool {
foreach ($this->backends as $backend) {
if ($backend->implementsActions(GroupInterface::ADD_TO_GROUP)) {
return true;
@@ -417,7 +377,7 @@ class Group implements IGroup {
*/
public function hideFromCollaboration(): bool {
return array_reduce($this->backends, function (bool $hide, GroupInterface $backend) {
- return $hide | ($backend instanceof IHideFromCollaborationBackend && $backend->hideGroup($this->gid));
+ return $hide || ($backend instanceof IHideFromCollaborationBackend && $backend->hideGroup($this->gid));
}, false);
}
}
diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php
index 28f7a400b41..e58a1fe6585 100644
--- a/lib/private/Group/Manager.php
+++ b/lib/private/Group/Manager.php
@@ -1,52 +1,28 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bart Visscher <bartv@thisnet.nl>
- * @author Bernhard Posselt <dev@bernhard-posselt.com>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Knut Ahlers <knut@ahlers.me>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author macjohnny <estebanmarin@gmx.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Robin McCorkell <robin@mccorkell.me.uk>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Roman Kreisel <mail@romankreisel.de>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- * @author Vincent Petry <vincent@nextcloud.com>
- * @author Vinicius Cubas Brand <vinicius@eita.org.br>
- * @author voxsim "Simon Vocella"
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Group;
use OC\Hooks\PublicEmitter;
+use OC\Settings\AuthorizedGroupMapper;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Group\Backend\IBatchMethodsBackend;
+use OCP\Group\Backend\ICreateNamedGroupBackend;
+use OCP\Group\Backend\IGroupDetailsBackend;
+use OCP\Group\Events\BeforeGroupCreatedEvent;
+use OCP\Group\Events\GroupCreatedEvent;
use OCP\GroupInterface;
+use OCP\ICacheFactory;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
+use OCP\Security\Ip\IRemoteAddress;
use Psr\Log\LoggerInterface;
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use function is_string;
/**
* Class Manager
@@ -67,48 +43,37 @@ class Manager extends PublicEmitter implements IGroupManager {
/** @var GroupInterface[] */
private $backends = [];
- /** @var \OC\User\Manager */
- private $userManager;
- /** @var EventDispatcherInterface */
- private $dispatcher;
- private LoggerInterface $logger;
-
- /** @var \OC\Group\Group[] */
+ /** @var array<string, IGroup> */
private $cachedGroups = [];
- /** @var (string[])[] */
+ /** @var array<string, list<string>> */
private $cachedUserGroups = [];
/** @var \OC\SubAdmin */
private $subAdmin = null;
- public function __construct(\OC\User\Manager $userManager,
- EventDispatcherInterface $dispatcher,
- LoggerInterface $logger) {
- $this->userManager = $userManager;
- $this->dispatcher = $dispatcher;
- $this->logger = $logger;
-
- $cachedGroups = &$this->cachedGroups;
- $cachedUserGroups = &$this->cachedUserGroups;
- $this->listen('\OC\Group', 'postDelete', function ($group) use (&$cachedGroups, &$cachedUserGroups) {
- /**
- * @var \OC\Group\Group $group
- */
- unset($cachedGroups[$group->getGID()]);
- $cachedUserGroups = [];
+ private DisplayNameCache $displayNameCache;
+
+ private const MAX_GROUP_LENGTH = 255;
+
+ public function __construct(
+ private \OC\User\Manager $userManager,
+ private IEventDispatcher $dispatcher,
+ private LoggerInterface $logger,
+ ICacheFactory $cacheFactory,
+ private IRemoteAddress $remoteAddress,
+ ) {
+ $this->displayNameCache = new DisplayNameCache($cacheFactory, $this);
+
+ $this->listen('\OC\Group', 'postDelete', function (IGroup $group): void {
+ unset($this->cachedGroups[$group->getGID()]);
+ $this->cachedUserGroups = [];
});
- $this->listen('\OC\Group', 'postAddUser', function ($group) use (&$cachedUserGroups) {
- /**
- * @var \OC\Group\Group $group
- */
- $cachedUserGroups = [];
+ $this->listen('\OC\Group', 'postAddUser', function (IGroup $group): void {
+ $this->cachedUserGroups = [];
});
- $this->listen('\OC\Group', 'postRemoveUser', function ($group) use (&$cachedUserGroups) {
- /**
- * @var \OC\Group\Group $group
- */
- $cachedUserGroups = [];
+ $this->listen('\OC\Group', 'postRemoveUser', function (IGroup $group): void {
+ $this->cachedUserGroups = [];
});
}
@@ -180,7 +145,7 @@ class Manager extends PublicEmitter implements IGroupManager {
if ($backend->implementsActions(Backend::GROUP_DETAILS)) {
$groupData = $backend->getGroupDetails($gid);
if (is_array($groupData) && !empty($groupData)) {
- // take the display name from the first backend that has a non-null one
+ // take the display name from the last backend that has a non-null one
if (is_null($displayName) && isset($groupData['displayName'])) {
$displayName = $groupData['displayName'];
}
@@ -193,11 +158,69 @@ class Manager extends PublicEmitter implements IGroupManager {
if (count($backends) === 0) {
return null;
}
+ /** @var GroupInterface[] $backends */
$this->cachedGroups[$gid] = new Group($gid, $backends, $this->dispatcher, $this->userManager, $this, $displayName);
return $this->cachedGroups[$gid];
}
/**
+ * @brief Batch method to create group objects
+ *
+ * @param list<string> $gids List of groupIds for which we want to create a IGroup object
+ * @param array<string, string> $displayNames Array containing already know display name for a groupId
+ * @return array<string, IGroup>
+ */
+ protected function getGroupsObjects(array $gids, array $displayNames = []): array {
+ $backends = [];
+ $groups = [];
+ foreach ($gids as $gid) {
+ $backends[$gid] = [];
+ if (!isset($displayNames[$gid])) {
+ $displayNames[$gid] = null;
+ }
+ }
+ foreach ($this->backends as $backend) {
+ if ($backend instanceof IGroupDetailsBackend || $backend->implementsActions(GroupInterface::GROUP_DETAILS)) {
+ /** @var IGroupDetailsBackend $backend */
+ if ($backend instanceof IBatchMethodsBackend) {
+ $groupDatas = $backend->getGroupsDetails($gids);
+ } else {
+ $groupDatas = [];
+ foreach ($gids as $gid) {
+ $groupDatas[$gid] = $backend->getGroupDetails($gid);
+ }
+ }
+ foreach ($groupDatas as $gid => $groupData) {
+ if (!empty($groupData)) {
+ // take the display name from the last backend that has a non-null one
+ if (isset($groupData['displayName'])) {
+ $displayNames[$gid] = $groupData['displayName'];
+ }
+ $backends[$gid][] = $backend;
+ }
+ }
+ } else {
+ if ($backend instanceof IBatchMethodsBackend) {
+ $existingGroups = $backend->groupsExists($gids);
+ } else {
+ $existingGroups = array_filter($gids, fn (string $gid): bool => $backend->groupExists($gid));
+ }
+ foreach ($existingGroups as $group) {
+ $backends[$group][] = $backend;
+ }
+ }
+ }
+ foreach ($gids as $gid) {
+ if (count($backends[$gid]) === 0) {
+ continue;
+ }
+ $this->cachedGroups[$gid] = new Group($gid, $backends[$gid], $this->dispatcher, $this->userManager, $this, $displayNames[$gid]);
+ $groups[$gid] = $this->cachedGroups[$gid];
+ }
+ return $groups;
+ }
+
+ /**
* @param string $gid
* @return bool
*/
@@ -214,12 +237,24 @@ class Manager extends PublicEmitter implements IGroupManager {
return null;
} elseif ($group = $this->get($gid)) {
return $group;
+ } elseif (mb_strlen($gid) > self::MAX_GROUP_LENGTH) {
+ throw new \Exception('Group name is limited to ' . self::MAX_GROUP_LENGTH . ' characters');
} else {
+ $this->dispatcher->dispatchTyped(new BeforeGroupCreatedEvent($gid));
$this->emit('\OC\Group', 'preCreate', [$gid]);
foreach ($this->backends as $backend) {
if ($backend->implementsActions(Backend::CREATE_GROUP)) {
- if ($backend->createGroup($gid)) {
+ if ($backend instanceof ICreateNamedGroupBackend) {
+ $groupName = $gid;
+ if (($gid = $backend->createGroup($groupName)) !== null) {
+ $group = $this->getGroupObject($gid);
+ $this->dispatcher->dispatchTyped(new GroupCreatedEvent($group));
+ $this->emit('\OC\Group', 'postCreate', [$group]);
+ return $group;
+ }
+ } elseif ($backend->createGroup($gid)) {
$group = $this->getGroupObject($gid);
+ $this->dispatcher->dispatchTyped(new GroupCreatedEvent($group));
$this->emit('\OC\Group', 'postCreate', [$group]);
return $group;
}
@@ -231,21 +266,17 @@ class Manager extends PublicEmitter implements IGroupManager {
/**
* @param string $search
- * @param int $limit
- * @param int $offset
+ * @param ?int $limit
+ * @param ?int $offset
* @return \OC\Group\Group[]
*/
- public function search($search, $limit = null, $offset = null) {
+ public function search(string $search, ?int $limit = null, ?int $offset = 0) {
$groups = [];
foreach ($this->backends as $backend) {
- $groupIds = $backend->getGroups($search, $limit, $offset);
- foreach ($groupIds as $groupId) {
- $aGroup = $this->get($groupId);
- if ($aGroup instanceof IGroup) {
- $groups[$groupId] = $aGroup;
- } else {
- $this->logger->debug('Group "' . $groupId . '" was returned by search but not found through direct access', ['app' => 'core']);
- }
+ $groupIds = $backend->getGroups($search, $limit ?? -1, $offset ?? 0);
+ $newGroups = $this->getGroupsObjects($groupIds);
+ foreach ($newGroups as $groupId => $group) {
+ $groups[$groupId] = $group;
}
if (!is_null($limit) and $limit <= 0) {
return array_values($groups);
@@ -258,7 +289,7 @@ class Manager extends PublicEmitter implements IGroupManager {
* @param IUser|null $user
* @return \OC\Group\Group[]
*/
- public function getUserGroups(IUser $user = null) {
+ public function getUserGroups(?IUser $user = null) {
if (!$user instanceof IUser) {
return [];
}
@@ -291,14 +322,30 @@ class Manager extends PublicEmitter implements IGroupManager {
* @return bool if admin
*/
public function isAdmin($userId) {
+ if (!$this->remoteAddress->allowsAdminActions()) {
+ return false;
+ }
+
foreach ($this->backends as $backend) {
- if ($backend->implementsActions(Backend::IS_ADMIN) && $backend->isAdmin($userId)) {
+ if (is_string($userId) && $backend->implementsActions(Backend::IS_ADMIN) && $backend->isAdmin($userId)) {
return true;
}
}
return $this->isInGroup($userId, 'admin');
}
+ public function isDelegatedAdmin(string $userId): bool {
+ if (!$this->remoteAddress->allowsAdminActions()) {
+ return false;
+ }
+
+ // Check if the user as admin delegation for users listing
+ $authorizedGroupMapper = \OCP\Server::get(AuthorizedGroupMapper::class);
+ $user = $this->userManager->get($userId);
+ $authorizedClasses = $authorizedGroupMapper->findAllClassesForUser($user);
+ return in_array(\OCA\Settings\Settings\Admin\Users::class, $authorizedClasses, true);
+ }
+
/**
* Checks if a userId is in a group
*
@@ -307,14 +354,14 @@ class Manager extends PublicEmitter implements IGroupManager {
* @return bool if in group
*/
public function isInGroup($userId, $group) {
- return array_search($group, $this->getUserIdGroupIds($userId)) !== false;
+ return in_array($group, $this->getUserIdGroupIds($userId));
}
/**
* get a list of group ids for a user
*
* @param IUser $user
- * @return string[] with group ids
+ * @return list<string> with group ids
*/
public function getUserGroupIds(IUser $user): array {
return $this->getUserIdGroupIds($user->getUID());
@@ -322,7 +369,7 @@ class Manager extends PublicEmitter implements IGroupManager {
/**
* @param string $uid the user id
- * @return string[]
+ * @return list<string>
*/
private function getUserIdGroupIds(string $uid): array {
if (!isset($this->cachedUserGroups[$uid])) {
@@ -339,6 +386,14 @@ class Manager extends PublicEmitter implements IGroupManager {
}
/**
+ * @param string $groupId
+ * @return ?string
+ */
+ public function getDisplayName(string $groupId): ?string {
+ return $this->displayNameCache->getDisplayName($groupId);
+ }
+
+ /**
* get an array of groupid and displayName for a user
*
* @param IUser $user
@@ -346,7 +401,7 @@ class Manager extends PublicEmitter implements IGroupManager {
*/
public function getUserGroupNames(IUser $user) {
return array_map(function ($group) {
- return ['displayName' => $group->getDisplayName()];
+ return ['displayName' => $this->displayNameCache->getDisplayName($group->getGID())];
}, $this->getUserGroups($user));
}
@@ -397,7 +452,7 @@ class Manager extends PublicEmitter implements IGroupManager {
$matchingUsers = [];
foreach ($groupUsers as $groupUser) {
- $matchingUsers[(string) $groupUser->getUID()] = $groupUser->getDisplayName();
+ $matchingUsers[(string)$groupUser->getUID()] = $groupUser->getDisplayName();
}
return $matchingUsers;
}
@@ -411,7 +466,7 @@ class Manager extends PublicEmitter implements IGroupManager {
$this->userManager,
$this,
\OC::$server->getDatabaseConnection(),
- \OC::$server->get(IEventDispatcher::class)
+ $this->dispatcher
);
}
diff --git a/lib/private/Group/MetaData.php b/lib/private/Group/MetaData.php
index a58d7e78bfc..77432eea5ff 100644
--- a/lib/private/Group/MetaData.php
+++ b/lib/private/Group/MetaData.php
@@ -1,31 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Stephan Peijnik <speijnik@anexia-it.com>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Group;
@@ -39,33 +17,22 @@ class MetaData {
public const SORT_USERCOUNT = 1; // May have performance issues on LDAP backends
public const SORT_GROUPNAME = 2;
- /** @var string */
- protected $user;
- /** @var bool */
- protected $isAdmin;
/** @var array */
protected $metaData = [];
- /** @var GroupManager */
- protected $groupManager;
/** @var int */
protected $sorting = self::SORT_NONE;
- /** @var IUserSession */
- protected $userSession;
/**
* @param string $user the uid of the current user
* @param bool $isAdmin whether the current users is an admin
*/
public function __construct(
- string $user,
- bool $isAdmin,
- IGroupManager $groupManager,
- IUserSession $userSession
- ) {
- $this->user = $user;
- $this->isAdmin = $isAdmin;
- $this->groupManager = $groupManager;
- $this->userSession = $userSession;
+ private string $user,
+ private bool $isAdmin,
+ private bool $isDelegatedAdmin,
+ private IGroupManager $groupManager,
+ private IUserSession $userSession,
+ ) {
}
/**
@@ -74,7 +41,7 @@ class MetaData {
* [0] array containing meta data about admin groups
* [1] array containing meta data about unprivileged groups
* @param string $groupSearch only effective when instance was created with
- * isAdmin being true
+ * isAdmin being true
* @param string $userSearch the pattern users are search for
*/
public function get(string $groupSearch = '', string $userSearch = ''): array {
@@ -184,11 +151,11 @@ class MetaData {
* @return IGroup[]
*/
public function getGroups(string $search = ''): array {
- if ($this->isAdmin) {
+ if ($this->isAdmin || $this->isDelegatedAdmin) {
return $this->groupManager->search($search);
} else {
$userObject = $this->userSession->getUser();
- if ($userObject !== null) {
+ if ($userObject !== null && $this->groupManager instanceof GroupManager) {
$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($userObject);
} else {
$groups = [];