aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Group/Manager.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Group/Manager.php')
-rw-r--r--lib/private/Group/Manager.php222
1 files changed, 132 insertions, 90 deletions
diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php
index b718afa5168..e58a1fe6585 100644
--- a/lib/private/Group/Manager.php
+++ b/lib/private/Group/Manager.php
@@ -1,53 +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
@@ -68,16 +43,10 @@ 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 */
@@ -85,35 +54,26 @@ class Manager extends PublicEmitter implements IGroupManager {
private DisplayNameCache $displayNameCache;
- public function __construct(\OC\User\Manager $userManager,
- EventDispatcherInterface $dispatcher,
- LoggerInterface $logger,
- ICacheFactory $cacheFactory) {
- $this->userManager = $userManager;
- $this->dispatcher = $dispatcher;
- $this->logger = $logger;
+ 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);
- $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 = [];
+ $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 = [];
});
}
@@ -185,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'];
}
@@ -198,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
*/
@@ -219,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;
}
@@ -236,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);
@@ -263,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 [];
}
@@ -296,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
*
@@ -312,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());
@@ -327,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])) {
@@ -410,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;
}
@@ -424,7 +466,7 @@ class Manager extends PublicEmitter implements IGroupManager {
$this->userManager,
$this,
\OC::$server->getDatabaseConnection(),
- \OC::$server->get(IEventDispatcher::class)
+ $this->dispatcher
);
}