diff --git a/.gitignore b/.gitignore index 643b389b6..fd6636480 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/ build vendor js/ +.idea/ diff --git a/lib/ACL/RuleManager.php b/lib/ACL/RuleManager.php index 2d35f5bfb..db619392c 100644 --- a/lib/ACL/RuleManager.php +++ b/lib/ACL/RuleManager.php @@ -21,23 +21,31 @@ namespace OCA\GroupFolders\ACL; +use OCA\GroupFolders\ACL\UserMapping\IEntityMappingManager; use OCA\GroupFolders\ACL\UserMapping\IUserMapping; -use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; class RuleManager { private $connection; - private $userMappingManager; - public function __construct(IDBConnection $connection, IUserMappingManager $userMappingManager) { + /** + * @var IEntityMappingManager + */ + private $entityMappingManager; + + + public function __construct( + IDBConnection $connection, + IEntityMappingManager $entityMappingManager + ) { $this->connection = $connection; - $this->userMappingManager = $userMappingManager; + $this->entityMappingManager = $entityMappingManager; } private function createRule(array $data): ?Rule { - $mapping = $this->userMappingManager->mappingFromId($data['mapping_type'], $data['mapping_id']); + $mapping = $this->entityMappingManager->mappingFromId($data['mapping_id']); if ($mapping) { return new Rule( $mapping, @@ -56,7 +64,7 @@ private function createRule(array $data): ?Rule { * @return (Rule[])[] [$fileId => Rule[]] */ public function getRulesForFilesById(IUser $user, array $fileIds): array { - $userMappings = $this->userMappingManager->getMappingsForUser($user); + $userMappings = $this->entityMappingManager->getMappingsForUser($user); $query = $this->connection->getQueryBuilder(); $query->select(['fileid', 'mapping_type', 'mapping_id', 'mask', 'permissions']) @@ -242,7 +250,7 @@ public function getAllRulesForPrefix(int $storageId, string $prefix): array { * @return array (Rule[])[] [$path => Rule[]] */ public function getRulesForPrefix(IUser $user, int $storageId, string $prefix): array { - $userMappings = $this->userMappingManager->getMappingsForUser($user); + $userMappings = $this->entityMappingManager->getMappingsForUser($user); $query = $this->connection->getQueryBuilder(); $query->select(['f.fileid', 'mapping_type', 'mapping_id', 'mask', 'a.permissions', 'path']) diff --git a/lib/ACL/UserMapping/EntityMappingManager.php b/lib/ACL/UserMapping/EntityMappingManager.php new file mode 100644 index 000000000..5d121871b --- /dev/null +++ b/lib/ACL/UserMapping/EntityMappingManager.php @@ -0,0 +1,136 @@ + + * + * @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 OCA\GroupFolders\ACL\UserMapping; + +use OCA\Circles\Exceptions\CircleNotFoundException; +use OCA\Circles\Exceptions\FederatedItemException; +use OCA\Circles\Exceptions\FederatedUserException; +use OCA\Circles\Exceptions\FederatedUserNotFoundException; +use OCA\Circles\Exceptions\InvalidIdException; +use OCA\Circles\Exceptions\MemberNotFoundException; +use OCA\Circles\Exceptions\OwnerNotFoundException; +use OCA\Circles\Exceptions\RemoteInstanceException; +use OCA\Circles\Exceptions\RemoteNotFoundException; +use OCA\Circles\Exceptions\RemoteResourceNotFoundException; +use OCA\Circles\Exceptions\RequestBuilderException; +use OCA\Circles\Exceptions\SingleCircleNotFoundException; +use OCA\Circles\Exceptions\UnknownRemoteException; +use OCA\Circles\Exceptions\UserTypeNotFoundException; +use OCA\Circles\Model\Member; +use OCA\Circles\Model\Probes\CircleProbe; +use OCP\Circles\ICirclesManager; +use OCP\IUser; + + +/** + * Class EntityMappingManager + * + * @package OCA\GroupFolders\ACL\UserMapping + */ +class EntityMappingManager implements IEntityMappingManager { + + /** @var ICirclesManager */ + private $circlesManager; + + + /** + * EntityMappingManager constructor. + * + * @param ICirclesManager $circlesManager + */ + public function __construct(ICirclesManager $circlesManager) { + $this->circlesManager = $circlesManager; + } + + + /** + * @param IUser $user + * @param bool $userAssignable + * + * @return array + * @throws CircleNotFoundException + * @throws FederatedItemException + * @throws FederatedUserException + * @throws FederatedUserNotFoundException + * @throws InvalidIdException + * @throws MemberNotFoundException + * @throws OwnerNotFoundException + * @throws RemoteInstanceException + * @throws RemoteNotFoundException + * @throws RemoteResourceNotFoundException + * @throws RequestBuilderException + * @throws SingleCircleNotFoundException + * @throws UnknownRemoteException + * @throws UserTypeNotFoundException + */ + public function getMappingsForUser(IUser $user, bool $userAssignable = true): array { + $federatedUser = $this->circlesManager->getFederatedUser($user->getUID(), Member::TYPE_USER); + + $mappings = []; + foreach ($federatedUser->getMemberships() as $membership) { + // TODO: lighten by providing details within getMemberships(); + $probe = new CircleProbe(); + $probe->includeSystemCircles(); + $probe->includeSingleCircles(); + $circle = $this->circlesManager->getCircle($membership->getCircleId(), $probe); + + $mappings[] = + new UserMapping( + 'entity', + $membership->getCircleId(), + $circle->getDisplayName() + ); + } + + return $mappings; + } + + + /** + * @param string $id + * + * @return IUserMapping|null + * @throws CircleNotFoundException + * @throws FederatedItemException + * @throws FederatedUserException + * @throws FederatedUserNotFoundException + * @throws InvalidIdException + * @throws MemberNotFoundException + * @throws OwnerNotFoundException + * @throws RemoteInstanceException + * @throws RemoteNotFoundException + * @throws RemoteResourceNotFoundException + * @throws RequestBuilderException + * @throws SingleCircleNotFoundException + * @throws UnknownRemoteException + * @throws UserTypeNotFoundException + */ + public function mappingFromId(string $id): ?IUserMapping { + $federatedUser = $this->circlesManager->getFederatedUser($id); + + return new UserMapping('entity', $id, $federatedUser->getDisplayName()); + } + +} diff --git a/lib/ACL/UserMapping/IEntityMappingManager.php b/lib/ACL/UserMapping/IEntityMappingManager.php new file mode 100644 index 000000000..4a5b282c1 --- /dev/null +++ b/lib/ACL/UserMapping/IEntityMappingManager.php @@ -0,0 +1,48 @@ + + * + * @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 OCA\GroupFolders\ACL\UserMapping; + +use OCP\IUser; + + +/** + * Interface IEntityMappingManager + * + * @package OCA\GroupFolders\ACL\UserMapping + */ +interface IEntityMappingManager { + /** + * @param IUser $user + * @param bool $userAssignable whether to include mappings that are assignable by non admin users + * @return IUserMapping[] + */ + public function getMappingsForUser(IUser $user, bool $userAssignable = true): array; + + /** + * @param string $id + * @return IUserMapping|null + */ + public function mappingFromId(string $id): ?IUserMapping; +} diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 42c8a2ec9..09fd75e8f 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -25,6 +25,8 @@ use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent; use OCA\GroupFolders\ACL\ACLManagerFactory; use OCA\GroupFolders\ACL\RuleManager; +use OCA\GroupFolders\ACL\UserMapping\EntityMappingManager; +use OCA\GroupFolders\ACL\UserMapping\IEntityMappingManager; use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager; use OCA\GroupFolders\ACL\UserMapping\UserMappingManager; use OCA\GroupFolders\CacheListener; @@ -127,7 +129,7 @@ public function register(IRegistrationContext $context): void { ); }); - $context->registerServiceAlias(IUserMappingManager::class, UserMappingManager::class); + $context->registerServiceAlias(IEntityMappingManager::class, EntityMappingManager::class); } public function boot(IBootContext $context): void { diff --git a/lib/Command/Group.php b/lib/Command/Group.php index 8c6208507..04e041483 100644 --- a/lib/Command/Group.php +++ b/lib/Command/Group.php @@ -22,7 +22,10 @@ namespace OCA\GroupFolders\Command; use OC\Core\Command\Base; +use OCA\Circles\CirclesManager; +use OCA\Circles\Model\Probes\CircleProbe; use OCA\GroupFolders\Folder\FolderManager; +use OCP\Circles\ICirclesManager; use OCP\Constants; use OCP\Files\IRootFolder; use OCP\IGroupManager; @@ -44,11 +47,20 @@ class Group extends Base { private $rootFolder; private $groupManager; - public function __construct(FolderManager $folderManager, IRootFolder $rootFolder, IGroupManager $groupManager) { + /** @var ICirclesManager */ + private $circlesManager; + + public function __construct( + FolderManager $folderManager, + IRootFolder $rootFolder, + IGroupManager $groupManager, + ICirclesManager $circlesManager + ) { parent::__construct(); $this->folderManager = $folderManager; $this->rootFolder = $rootFolder; $this->groupManager = $groupManager; + $this->circlesManager = $circlesManager; } protected function configure() { @@ -68,25 +80,32 @@ protected function execute(InputInterface $input, OutputInterface $output) { $folder = $this->folderManager->getFolder($folderId, $this->rootFolder->getMountPoint()->getNumericStorageId()); if ($folder) { $groupString = $input->getArgument('group'); - $group = $this->groupManager->get($groupString); + + // confirmation that $groupString is a valid CircleId + $this->circlesManager->startSuperSession(); + + $probe = new CircleProbe(); + $probe->includeSystemCircles(); + $this->circlesManager->getCircle($groupString); + +// $group = $this->groupManager->get($groupString); if ($input->getOption('delete')) { $this->folderManager->removeApplicableGroup($folderId, $groupString); return 0; - } elseif ($group) { - $permissionsString = $input->getArgument('permissions'); - $permissions = $this->getNewPermissions($permissionsString); - if ($permissions) { - if (!isset($folder['groups'][$groupString])) { - $this->folderManager->addApplicableGroup($folderId, $groupString); - } - $this->folderManager->setGroupPermissions($folderId, $groupString, $permissions); - return 0; - } else { - $output->writeln('Unable to parse permissions input: ' . implode(' ', $permissionsString) . ''); - return -1; + } + + $permissionsString = $input->getArgument('permissions'); + $permissions = $this->getNewPermissions($permissionsString); + if ($permissions) { + if (!isset($folder['groups'][$groupString])) { + $this->folderManager->addApplicableGroup($folderId, $groupString); } + $this->folderManager->setGroupPermissions($folderId, $groupString, $permissions); + + return 0; } else { - $output->writeln('group not found: ' . $groupString . ''); + $output->writeln('Unable to parse permissions input: ' . implode(' ', $permissionsString) . ''); + return -1; } } else { diff --git a/lib/Command/ListCommand.php b/lib/Command/ListCommand.php index 1f1c66059..2f70021d4 100644 --- a/lib/Command/ListCommand.php +++ b/lib/Command/ListCommand.php @@ -23,6 +23,7 @@ use OC\Core\Command\Base; use OCA\GroupFolders\Folder\FolderManager; +use OCP\Circles\ICirclesManager; use OCP\Constants; use OCP\Files\IRootFolder; use Symfony\Component\Console\Helper\Table; @@ -38,11 +39,13 @@ class ListCommand extends Base { ]; private $folderManager; + private $circlesManager; private $rootFolder; - public function __construct(FolderManager $folderManager, IRootFolder $rootFolder) { + public function __construct(FolderManager $folderManager, ICirclesManager $circlesManager, IRootFolder $rootFolder) { parent::__construct(); $this->folderManager = $folderManager; + $this->circlesManager = $circlesManager; $this->rootFolder = $rootFolder; } @@ -54,6 +57,7 @@ protected function configure() { } protected function execute(InputInterface $input, OutputInterface $output) { + $this->circlesManager->startSuperSession(); $folders = $this->folderManager->getAllFoldersWithSize($this->rootFolder->getMountPoint()->getNumericStorageId()); usort($folders, function ($a, $b) { return $a['id'] - $b['id']; @@ -73,12 +77,13 @@ protected function execute(InputInterface $input, OutputInterface $output) { $this->writeArrayInOutputFormat($input, $output, $folders); } else { $table = new Table($output); - $table->setHeaders(['Folder Id', 'Name', 'Groups', 'Quota', 'Size', 'Advanced Permissions', 'Manage advanced permissions']); + $table->setHeaders(['Folder Id', 'Name', 'Groups', 'Display Name', 'Quota', 'Size', 'Advanced Permissions', 'Manage advanced permissions']); $table->setRows(array_map(function ($folder) { $folder['size'] = \OCP\Util::humanFileSize($folder['size']); $folder['quota'] = ($folder['quota'] > 0) ? \OCP\Util::humanFileSize($folder['quota']) : 'Unlimited'; $groupStrings = array_map(function (string $groupId, int $permissions) { - return $groupId . ': ' . $this->permissionsToString($permissions); + $circle = $this->circlesManager->getCircle($groupId); + return $circle->getDisplayName(). ' (' . $groupId . ') : ' . $this->permissionsToString($permissions); }, array_keys($folder['groups']), array_values($folder['groups'])); $folder['groups'] = implode("\n", $groupStrings); $folder['acl'] = $folder['acl'] ? 'Enabled' : 'Disabled'; @@ -86,6 +91,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { return $manage['id'] . ' (' . $manage['type'] . ')'; }, $folder['manage']); $folder['manage'] = implode("\n", $manageStrings); + return $folder; }, $folders)); $table->render(); diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index cb220bf07..42b8cff32 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -26,11 +26,14 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\Files\IRootFolder; +use OCP\IGroupManager; use OCP\IRequest; class FolderController extends OCSController { /** @var FolderManager */ private $manager; + /** @var IGroupManager */ + private $groupManager; /** @var MountProvider */ private $mountProvider; /** @var IRootFolder */ @@ -42,12 +45,14 @@ public function __construct( $AppName, IRequest $request, FolderManager $manager, + IGroupManager $groupManager, MountProvider $mountProvider, IRootFolder $rootFolder, $userId ) { parent::__construct($AppName, $request); $this->manager = $manager; + $this->groupManager = $groupManager; $this->mountProvider = $mountProvider; $this->rootFolder = $rootFolder; $this->userId = $userId; @@ -209,22 +214,25 @@ private function folderDataForXML($data) { /** * @NoAdminRequired - * @param $id + * + * @param int $id * @param $fileId + * @param string $source * @param string $search + * * @return DataResponse */ - public function aclMappingSearch($id, $fileId, $search = ''): DataResponse { - $users = []; - $groups = []; - + public function aclMappingSearch(int $id, $fileId, string $source = '', string $search = ''): DataResponse { + $entities = []; if ($this->manager->canManageACL($id, $this->userId) === true) { - $groups = $this->manager->searchGroups($id, $search); - $users = $this->manager->searchUsers($id, $search); + $entities = $this->manager->searchEntities( + $id, + $search, + ($this->groupManager->isAdmin($this->userId) && $source === 'settings') + ); } return new DataResponse([ - 'users' => $users, - 'groups' => $groups, + 'entities' => $entities ]); } } diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index d4a17511d..51cd7a701 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -22,8 +22,25 @@ namespace OCA\GroupFolders\Folder; use OC\Files\Cache\Cache; +use OCA\Circles\Exceptions\CircleNotFoundException; +use OCA\Circles\Exceptions\FederatedItemException; +use OCA\Circles\Exceptions\FederatedUserException; +use OCA\Circles\Exceptions\FederatedUserNotFoundException; +use OCA\Circles\Exceptions\InitiatorNotFoundException; +use OCA\Circles\Exceptions\InvalidIdException; +use OCA\Circles\Exceptions\MemberNotFoundException; +use OCA\Circles\Exceptions\OwnerNotFoundException; +use OCA\Circles\Exceptions\RemoteInstanceException; +use OCA\Circles\Exceptions\RemoteNotFoundException; +use OCA\Circles\Exceptions\RemoteResourceNotFoundException; +use OCA\Circles\Exceptions\RequestBuilderException; +use OCA\Circles\Exceptions\SingleCircleNotFoundException; +use OCA\Circles\Exceptions\UnknownRemoteException; +use OCA\Circles\Exceptions\UserTypeNotFoundException; use OCA\GroupFolders\Mount\GroupFolderStorage; +use OCP\Circles\ICirclesManager; use OCP\Constants; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\IMimeTypeLoader; use OCP\IDBConnection; @@ -37,10 +54,18 @@ class FolderManager { /** @var IGroupManager */ private $groupManager; + /** @var ICirclesManager */ + private $circlesManager; + /** @var IMimeTypeLoader */ private $mimeTypeLoader; - public function __construct(IDBConnection $connection, IGroupManager $groupManager = null, IMimeTypeLoader $mimeTypeLoader = null) { + public function __construct( + IDBConnection $connection, + IGroupManager $groupManager = null, + ICirclesManager $circlesManager, + IMimeTypeLoader $mimeTypeLoader = null + ) { $this->connection = $connection; // files_fulltextsearch compatibility @@ -51,6 +76,7 @@ public function __construct(IDBConnection $connection, IGroupManager $groupManag $mimeTypeLoader = \OC::$server->getMimeTypeLoader(); } $this->groupManager = $groupManager; + $this->circlesManager = $circlesManager; $this->mimeTypeLoader = $mimeTypeLoader; } @@ -65,7 +91,7 @@ public function getAllFolders(): array { $query = $this->connection->getQueryBuilder(); $query->select('folder_id', 'mount_point', 'quota', 'acl') - ->from('group_folders', 'f'); + ->from('group_folders', 'f'); $rows = $query->execute()->fetchAll(); @@ -99,7 +125,7 @@ private function getGroupfolderRootId(int $rootStorageId): int { private function joinQueryWithFileCache(IQueryBuilder $query, int $rootStorageId): IQueryBuilder { return $query->leftJoin('f', 'filecache', 'c', $query->expr()->andX( // concat with empty string to work around missing cast to string - $query->expr()->eq('name', $query->func()->concat('f.folder_id', $query->expr()->literal(""))), + $query->expr()->eq('c.name', $query->func()->concat('f.folder_id', $query->expr()->literal(""))), $query->expr()->eq('parent', $query->createNamedParameter($this->getGroupfolderRootId($rootStorageId))) )); } @@ -115,7 +141,7 @@ public function getAllFoldersWithSize($rootStorageId): array { $query = $this->connection->getQueryBuilder(); $query->select('folder_id', 'mount_point', 'quota', 'size', 'acl') - ->from('group_folders', 'f'); + ->from('group_folders', 'f'); $this->joinQueryWithFileCache($query, $rootStorageId); $rows = $query->execute()->fetchAll(); @@ -146,19 +172,33 @@ public function getAllFoldersWithSize($rootStorageId): array { * @psalm-return array> */ private function getAllFolderMappings(): array { - $query = $this->connection->getQueryBuilder(); - $query->select('*') - ->from('group_folders_manage'); + $queryHelper = $this->circlesManager->getQueryHelper(); + $query = $queryHelper->getQueryBuilder(); + + $query->select('folder_id', 'mapping_type', 'mapping_id') + ->from('group_folders_manage', 'm'); + + $queryHelper->addCircleDetails('m', 'mapping_id'); + $rows = $query->execute()->fetchAll(); $folderMap = []; foreach ($rows as $row) { $id = (int)$row['folder_id']; + $circle = $queryHelper->extractCircle($row); + $data = [ + 'folder_id' => $row['folder_id'], + 'mapping_type' => $row['mapping_type'], + 'mapping_id' => $row['mapping_id'], + 'displayName' => $circle->getDisplayName(), + 'definition' => $this->circlesManager->getDefinition($circle) + ]; + if (!isset($folderMap[$id])) { - $folderMap[$id] = [$row]; + $folderMap[$id] = [$data]; } else { - $folderMap[$id][] = $row; + $folderMap[$id][] = $data; } } @@ -166,19 +206,30 @@ private function getAllFolderMappings(): array { } private function getManageAcl($mappings): array { - return array_filter(array_map(function ($entry) { - if ($entry['mapping_type'] === 'user') { - $user = \OC::$server->getUserManager()->get($entry['mapping_id']); - if ($user === null) { - return null; - } - return [ - 'type' => 'user', - 'id' => $user->getUID(), - 'displayname' => $user->getDisplayName() - ]; - } - $group = \OC::$server->getGroupManager()->get($entry['mapping_id']); + return array_filter( + array_map( + function ($entry) { + if ($entry['mapping_type'] === 'entity') { + return [ + 'type' => $entry['definition'], + 'id' => $entry['mapping_id'], + 'displayname' => $entry['displayName'] + ]; + } + + if ($entry['mapping_type'] === 'user') { + $user = \OC::$server->getUserManager()->get($entry['mapping_id']); + if ($user === null) { + return null; + } + + return [ + 'type' => 'user', + 'id' => $user->getUID(), + 'displayname' => $user->getDisplayName() + ]; + } + $group = \OC::$server->getGroupManager()->get($entry['mapping_id']); if ($group === null) { return []; } @@ -237,7 +288,7 @@ private function getAllApplicable(bool $permissionOnly = true): array { $query = $this->connection->getQueryBuilder(); $query->select('folder_id', 'group_id', 'permissions') - ->from('group_folders_groups'); + ->from('group_folders_groups'); $rows = $query->execute()->fetchAll(); @@ -253,81 +304,147 @@ private function getAllApplicable(bool $permissionOnly = true): array { return $applicableMap; } - private function getGroups($id): array { + + /** + * @param int $id + * @param bool $asAdmin + * + * @return array + * @throws InitiatorNotFoundException + * @throws RequestBuilderException + * @throws FederatedItemException + * @throws FederatedUserException + * @throws FederatedUserNotFoundException + * @throws InvalidIdException + * @throws RemoteInstanceException + * @throws RemoteNotFoundException + * @throws RemoteResourceNotFoundException + * @throws SingleCircleNotFoundException + * @throws UnknownRemoteException + */ + private function getEntities(int $id, bool $asAdmin = false): array { + if ($asAdmin) { + $this->circlesManager->startSuperSession(); + } else { + $this->circlesManager->startSession(); + } + $groups = $this->getAllApplicable()[$id] ?? []; - $groups = array_map(function ($gid) { - return $this->groupManager->get($gid); - }, array_keys($groups)); - return array_map(function ($group) { - return [ - 'gid' => $group->getGID(), - 'displayname' => $group->getDisplayName() + $entities = []; + foreach (array_keys($groups) as $gid) { + try { + $circle = $this->circlesManager->getCircle($gid); + } catch (CircleNotFoundException $e) { + continue; + } + + $entities[$circle->getSingleId()] = [ + 'singleId' => $circle->getSingleId(), + 'displayName' => $circle->getDisplayName(), + 'definition' => $this->circlesManager->getDefinition($circle) ]; - }, array_filter($groups)); + foreach ($circle->getInheritedMembers(false) as $member) { + if ($member->hasBasedOn()) { + $basedOn = $member->getBasedOn(); + $entities[$basedOn->getSingleId()] = [ + 'singleId' => $basedOn->getSingleId(), + 'displayName' => $basedOn->getDisplayName(), + 'definition' => $this->circlesManager->getDefinition($basedOn) + ]; + } else { + $entities[$member->getSingleId()] = [ + 'singleId' => $member->getSingleId(), + 'displayName' => $member->getDisplayName(), + 'definition' => $this->circlesManager->getDefinition($member) + ]; + } + } + } + + return array_values($entities); } + + /** + * @param $folderId + * @param $userId + * + * @return bool + * @throws CircleNotFoundException + * @throws Exception + * @throws FederatedItemException + * @throws FederatedUserException + * @throws FederatedUserNotFoundException + * @throws InvalidIdException + * @throws RemoteInstanceException + * @throws RemoteNotFoundException + * @throws RemoteResourceNotFoundException + * @throws RequestBuilderException + * @throws SingleCircleNotFoundException + * @throws UnknownRemoteException + * @throws MemberNotFoundException + * @throws OwnerNotFoundException + * @throws UserTypeNotFoundException + */ public function canManageACL($folderId, $userId): bool { if ($this->groupManager->isAdmin($userId)) { return true; } - $query = $this->connection->getQueryBuilder(); - $query->select('*') - ->from('group_folders_manage') - ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))) - ->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('user'))) - ->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($userId))); - if ($query->execute()->rowCount() === 1) { - return true; - } + $this->circlesManager->startUserSession($userId); + $queryHelper = $this->circlesManager->getQueryHelper(); + $query = $queryHelper->getQueryBuilder(); - $query = $this->connection->getQueryBuilder(); - $query->select('*') - ->from('group_folders_manage') - ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))) - ->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('group'))); - $groups = $query->execute()->fetchAll(); - foreach ($groups as $manageRule) { - if ($this->groupManager->isInGroup($userId, $manageRule['mapping_id'])) { - return true; - } - } - return false; + $query->select('folder_id', 'mapping_type', 'mapping_id') + ->from('group_folders_manage', 'm') + ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))) + ->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('entity'))); + + $queryHelper->limitToSession('m', 'mapping_id'); + + return ($query->execute()->rowCount() > 0); } - public function searchGroups($id, $search = ''): array { - $groups = $this->getGroups($id); + public function searchEntities($id, $search = '', bool $asAdmin = false): array { + $groups = $this->getEntities($id, $asAdmin); if ($search === '') { return $groups; } - return array_filter($groups, function ($group) use ($search) { - return (stripos($group['gid'], $search) !== false) || (stripos($group['displayname'], $search) !== false); - }); - } - public function searchUsers($id, $search = '', $limit = 10, $offset = 0): array { - $groups = $this->getGroups($id); - $users = []; - foreach ($groups as $groupArray) { - $group = $this->groupManager->get($groupArray['gid']); - if ($group) { - $foundUsers = $this->groupManager->displayNamesInGroup($group->getGID(), $search, $limit, $offset); - foreach ($foundUsers as $uid => $displayName) { - if (!isset($users[$uid])) { - $users[$uid] = [ - 'uid' => $uid, - 'displayname' => $displayName - ]; - } + return array_values( + array_filter( + $groups, + function ($group) use ($search) { + return (stripos($group['displayName'], $search) !== false); } - } - } - return array_values($users); + ) + ); } +// public function searchUsers($id, $search = '', $limit = 10, $offset = 0): array { +// $groups = $this->getEntities($id); +// $users = []; +// foreach ($groups as $groupArray) { +// $group = $this->groupManager->get($groupArray['gid']); +// if ($group) { +// $foundUsers = $this->groupManager->displayNamesInGroup($group->getGID(), $search, $limit, $offset); +// foreach ($foundUsers as $uid => $displayName) { +// if (!isset($users[$uid])) { +// $users[$uid] = [ +// 'uid' => $uid, +// 'displayname' => $displayName +// ]; +// } +// } +// } +// } +// return array_values($users); +// } + /** * @param string $groupId * @param int $rootStorageId + * * @return array[] */ public function getFoldersForGroup($groupId, $rootStorageId = 0) { @@ -351,16 +468,16 @@ public function getFoldersForGroup($groupId, $rootStorageId = 0) { 'encrypted', 'parent' ) - ->selectAlias('a.permissions', 'group_permissions') - ->selectAlias('c.permissions', 'permissions') - ->from('group_folders', 'f') - ->innerJoin( - 'f', - 'group_folders_groups', - 'a', - $query->expr()->eq('f.folder_id', 'a.folder_id') - ) - ->where($query->expr()->eq('a.group_id', $query->createNamedParameter($groupId))); + ->selectAlias('a.permissions', 'group_permissions') + ->selectAlias('c.permissions', 'permissions') + ->from('group_folders', 'f') + ->innerJoin( + 'f', + 'group_folders_groups', + 'a', + $query->expr()->eq('f.folder_id', 'a.folder_id') + ) + ->where($query->expr()->eq('a.group_id', $query->createNamedParameter($groupId))); $this->joinQueryWithFileCache($query, $rootStorageId); $result = $query->execute()->fetchAll(); @@ -402,12 +519,12 @@ public function getFoldersForGroups(array $groupIds, $rootStorageId = 0): array 'encrypted', 'parent' ) - ->selectAlias('a.permissions', 'group_permissions') - ->selectAlias('c.permissions', 'permissions') - ->from('group_folders', 'f') - ->innerJoin( - 'f', - 'group_folders_groups', + ->selectAlias('a.permissions', 'group_permissions') + ->selectAlias('c.permissions', 'permissions') + ->from('group_folders', 'f') + ->innerJoin( + 'f', + 'group_folders_groups', 'a', $query->expr()->eq('f.folder_id', 'a.folder_id') ) @@ -427,6 +544,67 @@ public function getFoldersForGroups(array $groupIds, $rootStorageId = 0): array }, $result); } + + /** + * @param string[] $groupId + * @param int $rootStorageId + * @return array[] + */ + public function getFolders($rootStorageId = 0): array { + if (!$this->circlesManager->isSessionInitiated()) { + $this->circlesManager->startSession(); + } + + $queryHelper = $this->circlesManager->getQueryHelper(); + $query = $queryHelper->getQueryBuilder(); + + $query->select( + 'f.folder_id', + 'mount_point', + 'quota', + 'acl', + 'fileid', + 'storage', + 'path', + 'c.name', + 'mimetype', + 'mimepart', + 'size', + 'mtime', + 'storage_mtime', + 'etag', + 'encrypted', + 'parent' + ) + ->selectAlias('a.permissions', 'group_permissions') + ->selectAlias('c.permissions', 'permissions') + ->from('group_folders', 'f') + ->innerJoin( + 'f', + 'group_folders_groups', + 'a', + $query->expr()->eq('f.folder_id', 'a.folder_id') + ); + +// ->where($query->expr()->in('a.group_id', $query->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY))); + $queryHelper->limitToSession('a', 'group_id', false); + + $this->joinQueryWithFileCache($query, $rootStorageId); + + $result = $query->execute()->fetchAll(); + return array_map(function ($folder) { + return [ + 'folder_id' => (int)$folder['folder_id'], + 'mount_point' => $folder['mount_point'], + 'permissions' => (int)$folder['group_permissions'], + 'quota' => (int)$folder['quota'], + 'acl' => (bool)$folder['acl'], + 'rootCacheEntry' => (isset($folder['fileid'])) ? Cache::cacheEntryFromData($folder, $this->mimeTypeLoader) : null + ]; + }, $result); + } + + public function createFolder($mountPoint) { $query = $this->connection->getQueryBuilder(); @@ -481,6 +659,7 @@ public function setGroupPermissions($folderId, $groupId, $permissions): void { } public function setManageACL($folderId, $type, $id, $manageAcl): void { + $type = 'entity'; $query = $this->connection->getQueryBuilder(); if ($manageAcl === true) { $query->insert('group_folders_manage') @@ -492,8 +671,8 @@ public function setManageACL($folderId, $type, $id, $manageAcl): void { } else { $query->delete('group_folders_manage') ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))) - ->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter($type))) - ->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($id))); + ->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter($type))) + ->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($id))); } $query->execute(); } @@ -510,8 +689,8 @@ public function setFolderQuota($folderId, $quota): void { $query = $this->connection->getQueryBuilder(); $query->update('group_folders') - ->set('quota', $query->createNamedParameter($quota)) - ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))); + ->set('quota', $query->createNamedParameter($quota)) + ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))); $query->execute(); } @@ -528,7 +707,7 @@ public function deleteGroup($groupId): void { $query = $this->connection->getQueryBuilder(); $query->delete('group_folders_groups') - ->where($query->expr()->eq('group_id', $query->createNamedParameter($groupId))); + ->where($query->expr()->eq('group_id', $query->createNamedParameter($groupId))); $query->execute(); } @@ -536,14 +715,14 @@ public function setFolderACL($folderId, bool $acl): void { $query = $this->connection->getQueryBuilder(); $query->update('group_folders') - ->set('acl', $query->createNamedParameter((int)$acl, IQueryBuilder::PARAM_INT)) - ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))); + ->set('acl', $query->createNamedParameter((int)$acl, IQueryBuilder::PARAM_INT)) + ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))); $query->execute(); if ($acl === false) { $query = $this->connection->getQueryBuilder(); $query->delete('group_folders_manage') - ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))); + ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))); $query->execute(); } } @@ -551,11 +730,27 @@ public function setFolderACL($folderId, bool $acl): void { /** * @param IUser $user * @param int $rootStorageId + * * @return array[] + * @throws CircleNotFoundException + * @throws Exception + * @throws FederatedItemException + * @throws FederatedUserException + * @throws FederatedUserNotFoundException + * @throws InvalidIdException + * @throws RemoteInstanceException + * @throws RemoteNotFoundException + * @throws RemoteResourceNotFoundException + * @throws RequestBuilderException + * @throws SingleCircleNotFoundException + * @throws UnknownRemoteException */ - public function getFoldersForUser(IUser $user, $rootStorageId = 0) { - $groups = $this->groupManager->getUserGroupIds($user); - $folders = $this->getFoldersForGroups($groups, $rootStorageId); + public function getFoldersForUser(IUser $user, int $rootStorageId = 0): array { +// $groups = $this->groupManager->getUserGroupIds($user); +// $folders = $this->getFoldersForGroups($groups, $rootStorageId); + + $this->circlesManager->startUserSession($user->getUID()); + $folders = $this->getFolders(); $mergedFolders = []; foreach ($folders as $folder) { diff --git a/src/components/SharingSidebarView.vue b/src/components/SharingSidebarView.vue index c02b0d872..3506de425 100644 --- a/src/components/SharingSidebarView.vue +++ b/src/components/SharingSidebarView.vue @@ -236,11 +236,7 @@ export default { }) }, getFullDisplayName(displayName, type) { - if (type === 'group') { - return t('groupfolders', '{displayName} (Group)', { displayName }) - } - - return displayName + return displayName + ' (' + type + ')' }, searchMappings(query) { if (searchRequestCancelSource) { @@ -248,27 +244,21 @@ export default { } searchRequestCancelSource = axios.CancelToken.source() this.isSearching = true + axios.get(generateUrl(`apps/groupfolders/folders/${this.groupFolderId}/search`) + '?format=json&search=' + query, { cancelToken: searchRequestCancelSource.token, }).then((result) => { this.isSearching = false - const groups = Object.values(result.data.ocs.data.groups).map((group) => { + const entities = Object.values(result.data.ocs.data.entities).map((entity) => { return { - unique: 'group:' + group.gid, - type: 'group', - id: group.gid, - displayname: group.displayname, + unique: entity.singleId, + id: entity.singleId, + displayname: entity.displayName, + type: entity.definition } }) - const users = Object.values(result.data.ocs.data.users).map((user) => { - return { - unique: 'user:' + user.uid, - type: 'user', - id: user.uid, - displayname: user.displayname, - } - }) - this.options = [...groups, ...users].filter((entry) => { + + this.options = entities.filter((entry) => { // filter out existing acl rules return !this.list.find((existingAcl) => entry.unique === existingAcl.getUniqueMappingIdentifier()) }) diff --git a/src/settings/Api.ts b/src/settings/Api.ts index a2ac02510..176a873ac 100644 --- a/src/settings/Api.ts +++ b/src/settings/Api.ts @@ -7,6 +7,13 @@ export interface Group { displayname: string; } +export interface Entity { + singleId: string; + displayName: string; + definition: string; + type: string; +} + export interface OCSUser { uid: string; displayname: string; @@ -46,7 +53,15 @@ export class Api { listGroups(): Thenable { const version = parseInt(OC.config.version, 10); - if (version >= 14) { + if (version >= 22) { + return $.getJSON(OC.linkToOCS('cloud', 1) + 'admin/entities/details?filter=-single') + .then((data: OCSResult<{ entities: Entity[]; }>) => data.ocs.data.entities.map(entity => { + return { + id: entity.singleId, + displayname: entity.displayName + ' (' + entity.definition + ')' + }; + })); + } else if (version >= 14) { return $.getJSON(OC.linkToOCS('cloud', 1) + 'groups/details') .then((data: OCSResult<{ groups: Group[]; }>) => data.ocs.data.groups); } else { @@ -118,24 +133,19 @@ export class Api { }); } - aclMappingSearch(folderId: number, search: string): Thenable<{groups: OCSGroup[], users: OCSUser[]}> { - return $.getJSON(this.getUrl(`folders/${folderId}/search?format=json&search=${search}`)) - .then((data: OCSResult<{ groups: OCSGroup[]; users: OCSUser[]; }>) => { + aclMappingSearch(folderId: number, search: string): Thenable<{groups: OCSGroup[], users: OCSUser[], entities: Entity[]}> { + return $.getJSON(this.getUrl(`folders/${folderId}/search?format=json&source=settings&search=${search}`)) + .then((data: OCSResult<{ entities: Entity[]; groups: OCSGroup; users: OCSUser; }>) => { return { - groups: Object.values(data.ocs.data.groups).map((item) => { + entities: Object.values(data.ocs.data.entities).map((item) => { return { - type: 'group', - id: item.gid, - displayname: item.displayname + type: item.definition, + id: item.singleId, + displayname: item.displayName } }), - users: Object.values(data.ocs.data.users).map((item) => { - return { - type: 'user', - id: item.uid, - displayname: item.displayname - } - }) + groups: [], + users: [] } }); } diff --git a/src/settings/App.tsx b/src/settings/App.tsx index e6743fc84..a28dc9278 100644 --- a/src/settings/App.tsx +++ b/src/settings/App.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import {ChangeEvent, Component, FormEvent} from 'react'; -import {Api, Folder, Group, ManageRuleProps, OCSGroup, OCSUser} from './Api'; +import {Api, Entity, Folder, Group, ManageRuleProps, OCSGroup, OCSUser} from './Api'; import {FolderGroups} from './FolderGroups'; import {QuotaSelect} from './QuotaSelect'; import './App.scss'; @@ -320,7 +320,7 @@ export class App extends Component<{}, AppState> implements OC.Plugin void; - onSearch: (name: string) => Thenable<{ groups: OCSGroup[]; users: OCSUser[]; }>; + onSearch: (name: string) => Thenable<{ groups: OCSGroup[]; users: OCSUser[]; entities: Entity[]; }>; }; @@ -329,13 +329,14 @@ function ManageAclSelect({onChange, onSearch, folder}: ManageAclSelectProps) { const handleSearch = (inputValue: string) => { return new Promise(resolve => { onSearch(inputValue).then((result) => { - resolve([...result.groups, ...result.users]) + resolve([...result.groups, ...result.users, ...result.entities]) }) }) } const typeLabel = (item) => { - return item.type === 'user' ? 'User' : 'Group' + // return item.type === 'user' ? 'User' : 'Group' + return item.type } return