aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Group/Database.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Group/Database.php')
-rw-r--r--lib/private/Group/Database.php279
1 files changed, 177 insertions, 102 deletions
diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php
index c49f3bce596..0cb571a3935 100644
--- a/lib/private/Group/Database.php
+++ b/lib/private/Group/Database.php
@@ -1,89 +1,58 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
- * @author Loki3000 <github@labcms.ru>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @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/>
- *
- */
-/*
- *
- * The following SQL statement is just a help for developers and will not be
- * executed!
- *
- * CREATE TABLE `groups` (
- * `gid` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
- * PRIMARY KEY (`gid`)
- * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
- *
- * CREATE TABLE `group_user` (
- * `gid` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
- * `uid` varchar(64) COLLATE utf8_unicode_ci NOT NULL
- * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
- *
+ * 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\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 {
-
- /** @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,
+ ) {
}
/**
@@ -95,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;
}
/**
@@ -140,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]);
@@ -177,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();
@@ -202,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;
@@ -224,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;
}
@@ -232,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.
@@ -251,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()) {
@@ -275,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');
@@ -292,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();
@@ -322,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();
@@ -337,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(
@@ -378,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();
@@ -409,7 +423,7 @@ class Database extends ABackend implements
)));
}
- $result = $query->execute();
+ $result = $query->executeQuery();
$count = $result->fetchOne();
$result->closeCursor();
@@ -441,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();
@@ -456,7 +470,11 @@ class Database extends ABackend implements
public function getDisplayName(string $gid): string {
if (isset($this->groupCache[$gid])) {
- return $this->groupCache[$gid]['displayname'];
+ $displayName = $this->groupCache[$gid]['displayname'];
+
+ if (isset($displayName) && trim($displayName) !== '') {
+ return $displayName;
+ }
}
$this->fixDI();
@@ -466,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 {
@@ -482,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;
@@ -498,8 +555,26 @@ 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;
}
+
+ /**
+ * Backend name to be shown in group management
+ * @return string the name of the backend to be shown
+ * @since 21.0.0
+ */
+ 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;
+ }
}