* @author Joas Schilling * @author John Molakvoæ (skjnldsv) * @author Loki3000 * @author Morris Jobke * @author Roeland Jago Douma * * @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 * */ /* * * 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; * */ namespace OC\Group; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Group\Backend\ABackend; use OCP\Group\Backend\IAddToGroupBackend; use OCP\Group\Backend\ICountDisabledInGroup; use OCP\Group\Backend\ICountUsersBackend; use OCP\Group\Backend\ICreateGroupBackend; use OCP\Group\Backend\IDeleteGroupBackend; use OCP\Group\Backend\IGetDisplayNameBackend; use OCP\Group\Backend\IGroupDetailsBackend; use OCP\Group\Backend\IRemoveFromGroupBackend; use OCP\Group\Backend\ISetDisplayNameBackend; use OCP\IDBConnection; /** * 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[] */ private $groupCache = []; /** @var IDBConnection */ private $dbConn; /** * \OC\Group\Database constructor. * * @param IDBConnection|null $dbConn */ public function __construct(IDBConnection $dbConn = null) { $this->dbConn = $dbConn; } /** * FIXME: This function should not be required! */ private function fixDI() { if ($this->dbConn === null) { $this->dbConn = \OC::$server->getDatabaseConnection(); } } /** * 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 { $this->fixDI(); try { // Add group $builder = $this->dbConn->getQueryBuilder(); $result = $builder->insert('groups') ->setValue('gid', $builder->createNamedParameter($gid)) ->setValue('displayname', $builder->createNamedParameter($gid)) ->execute(); } catch(UniqueConstraintViolationException $e) { $result = 0; } // Add to cache $this->groupCache[$gid] = $gid; return $result === 1; } /** * delete a group * @param string $gid gid of the group to delete * @return bool * * Deletes a group and removes it from the group_user-table */ public function deleteGroup(string $gid): bool { $this->fixDI(); // Delete the group $qb = $this->dbConn->getQueryBuilder(); $qb->delete('groups') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); // Delete the group-user relation $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_user') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); // Delete the group-groupadmin relation $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_admin') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); // Delete from cache unset($this->groupCache[$gid]); return true; } /** * is user in group? * @param string $uid uid of the user * @param string $gid gid of the group * @return bool * * Checks whether the user is member of a group or not. */ public function inGroup($uid, $gid) { $this->fixDI(); // check $qb = $this->dbConn->getQueryBuilder(); $cursor = $qb->select('uid') ->from('group_user') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->execute(); $result = $cursor->fetch(); $cursor->closeCursor(); return $result ? true : false; } /** * Add a user to a group * @param string $uid Name of the user to add to group * @param string $gid Name of the group in which add the user * @return bool * * Adds a user to a group. */ public function addToGroup(string $uid, string $gid): bool { $this->fixDI(); // No duplicate entries! if(!$this->inGroup($uid, $gid)) { $qb = $this->dbConn->getQueryBuilder(); $qb->insert('group_user') ->setValue('uid', $qb->createNamedParameter($uid)) ->setValue('gid', $qb->createNamedParameter($gid)) ->execute(); return true; }else{ return false; } } /** * Removes a user from a group * @param string $uid Name of the user to remove from group * @param string $gid Name of the group from which remove the user * @return bool * * removes the user from a group. */ public function removeFromGroup(string $uid, string $gid): bool { $this->fixDI(); $qb = $this->dbConn->getQueryBuilder(); $qb->delete('group_user') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->andWhere($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); return true; } /** * Get all groups a user belongs to * @param string $uid Name of the user * @return array an array of group names * * This function fetches all groups a user belongs to. It does not check * if the user exists at all. */ public function getUserGroups($uid) { //guests has empty or null $uid if ($uid === null || $uid === '') { return []; } $this->fixDI(); // No magic! $qb = $this->dbConn->getQueryBuilder(); $cursor = $qb->select('gid') ->from('group_user') ->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->execute(); $groups = []; while($row = $cursor->fetch()) { $groups[] = $row['gid']; $this->groupCache[$row['gid']] = $row['gid']; } $cursor->closeCursor(); return $groups; } /** * get a list of all groups * @param string $search * @param int $limit * @param int $offset * @return array an array of group names * * Returns a list with all groups */ public function getGroups($search = '', $limit = null, $offset = null) { $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); $query->select('gid') ->from('groups') ->orderBy('gid', 'ASC'); if ($search !== '') { $query->where($query->expr()->iLike('gid', $query->createNamedParameter( '%' . $this->dbConn->escapeLikeParameter($search) . '%' ))); } $query->setMaxResults($limit) ->setFirstResult($offset); $result = $query->execute(); $groups = []; while ($row = $result->fetch()) { $groups[] = $row['gid']; } $result->closeCursor(); return $groups; } /** * check if a group exists * @param string $gid * @return bool */ public function groupExists($gid) { $this->fixDI(); // Check cache first if (isset($this->groupCache[$gid])) { return true; } $qb = $this->dbConn->getQueryBuilder(); $cursor = $qb->select('gid') ->from('groups') ->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid))) ->execute(); $result = $cursor->fetch(); $cursor->closeCursor(); if ($result !== false) { $this->groupCache[$gid] = $gid; return true; } return false; } /** * 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 */ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); $query->select('uid') ->from('group_user') ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))) ->orderBy('uid', 'ASC'); if ($search !== '') { $query->andWhere($query->expr()->like('uid', $query->createNamedParameter( '%' . $this->dbConn->escapeLikeParameter($search) . '%' ))); } if ($limit !== -1) { $query->setMaxResults($limit); } if ($offset !== 0) { $query->setFirstResult($offset); } $result = $query->execute(); $users = []; while ($row = $result->fetch()) { $users[] = $row['uid']; } $result->closeCursor(); return $users; } /** * get the number of all users matching the search string in a group * @param string $gid * @param string $search * @return int */ public function countUsersInGroup(string $gid, string $search = ''): int { $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); $query->select($query->func()->count('*', 'num_users')) ->from('group_user') ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))); if ($search !== '') { $query->andWhere($query->expr()->like('uid', $query->createNamedParameter( '%' . $this->dbConn->escapeLikeParameter($search) . '%' ))); } $result = $query->execute(); $count = $result->fetchColumn(); $result->closeCursor(); if ($count !== false) { $count = (int)$count; } else { $count = 0; } return $count; } /** * get the number of disabled users in a group * * @param string $search * * @return int */ public function countDisabledInGroup(string $gid): int { $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); $query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')')) ->from('preferences', 'p') ->innerJoin('p', 'group_user', 'g', $query->expr()->eq('p.userid', 'g.uid')) ->where($query->expr()->eq('appid', $query->createNamedParameter('core'))) ->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('enabled'))) ->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(); $count = $result->fetchColumn(); $result->closeCursor(); if ($count !== false) { $count = (int)$count; } else { $count = 0; } return $count; } public function getDisplayName(string $gid): string { $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); $query->select('displayname') ->from('groups') ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))); $result = $query->execute(); $displayName = $result->fetchColumn(); $result->closeCursor(); return (string) $displayName; } public function getGroupDetails(string $gid): array { $displayName = $this->getDisplayName($gid); if ($displayName !== '') { return ['displayName' => $displayName]; } return []; } public function setDisplayName(string $gid, string $displayName): bool { if (!$this->groupExists($gid)) { return false; } $this->fixDI(); $displayName = trim($displayName); if ($displayName === '') { $displayName = $gid; } $query = $this->dbConn->getQueryBuilder(); $query->update('groups') ->set('displayname', $query->createNamedParameter($displayName)) ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))); $query->execute(); return true; } }