From 3ea51c9122a7ab74a91d83405caeaf55aafffd94 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Wed, 9 Sep 2026 17:00:47 +0200 Subject: [PATCH 1/2] enh(performance): get deck share permissions in one query Signed-off-by: grnd-alt --- lib/Db/AclMapper.php | 27 ++++++++++++++++++++++++ lib/Service/PermissionService.php | 25 +++++++++++++++++++++++ lib/Sharing/DeckShareProvider.php | 34 +++++++++++++++---------------- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/lib/Db/AclMapper.php b/lib/Db/AclMapper.php index f85d7bf699..7a5a4ef3d3 100644 --- a/lib/Db/AclMapper.php +++ b/lib/Db/AclMapper.php @@ -33,6 +33,33 @@ public function findAll(int $boardId, ?int $limit = null, ?int $offset = null) { return $this->findEntities($qb); } + /** + * @return array Acl entries grouped by card id + * @throws \OCP\DB\Exception + */ + public function findInCards(array $cardIds, ?int $limit = null, ?int $offset = null): array { + $qb = $this->db->getQueryBuilder(); + $qb->select('acl.id', 'acl.board_id', 'acl.type', 'acl.participant', 'acl.permission_edit', 'acl.permission_share', 'acl.permission_manage', 'acl.created_at', 'acl.last_modified_at', 'c.id AS card_id') + ->from('deck_board_acl', 'acl') + ->innerJoin('acl', 'deck_boards', 'b', 'acl.board_id = b.id') + ->innerJoin('b', 'deck_stacks', 's', 's.board_id = b.id') + ->innerJoin('s', 'deck_cards', 'c', 'c.stack_id = s.id') + ->where($qb->expr()->in('c.id', $qb->createParameter('cardIds'))) + ->setMaxResults($limit) + ->setFirstResult($offset); + + $aclsByCardId = []; + foreach ($this->chunkQuery($cardIds, function (array $ids) use ($qb) { + $qb->setParameter('cardIds', $ids, IQueryBuilder::PARAM_INT_ARRAY); + return $qb->executeQuery()->fetchAll(); + }) as $row) { + $cardId = (int)$row['card_id']; + unset($row['card_id']); + $aclsByCardId[$cardId][] = Acl::fromRow($row); + } + return $aclsByCardId; + } + public function findIn(array $boardIds, ?int $limit = null, ?int $offset = null): array { $qb = $this->db->getQueryBuilder(); $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage') diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php index 775e7e048f..f25b68a8fc 100644 --- a/lib/Service/PermissionService.php +++ b/lib/Service/PermissionService.php @@ -49,6 +49,31 @@ public function __construct( $this->permissionCache = new CappedMemoryCache(); } + /** + * Get permissions for all supplied card ids + * @param int[] $cardIds + * @param ?string $userId + * @param bool $allowDeleted + * @return array> + */ + public function getPermissionsForCards(array $cardIds, ?string $userId = null, bool $allowDeleted = false): array { + if ($userId === null) { + $userId = $this->userId; + } + $permissions = $this->aclMapper->findInCards($cardIds); + return array_map( + function (array $acls) use ($userId) { + return [ + Acl::PERMISSION_READ => $this->userCan($acls, Acl::PERMISSION_READ, $userId), + Acl::PERMISSION_EDIT => $this->userCan($acls, Acl::PERMISSION_EDIT, $userId), + Acl::PERMISSION_MANAGE => $this->userCan($acls, Acl::PERMISSION_MANAGE, $userId), + Acl::PERMISSION_SHARE => $this->userCan($acls, Acl::PERMISSION_SHARE, $userId), + ]; + }, + $permissions + ); + } + /** * Get current user permissions for a board by id * diff --git a/lib/Sharing/DeckShareProvider.php b/lib/Sharing/DeckShareProvider.php index 37361e28c3..f5337a4590 100644 --- a/lib/Sharing/DeckShareProvider.php +++ b/lib/Sharing/DeckShareProvider.php @@ -269,22 +269,6 @@ private function createShareObject(array $data): IShare { return $share; } - private function applyBoardPermission($share, $permissions, $userId) { - try { - $this->permissionService->checkPermission($this->cardMapper, $share->getSharedWith(), Acl::PERMISSION_EDIT, $userId, true); - } catch (NoPermissionException $e) { - $permissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE; - $permissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE; - $permissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE; - } - - try { - $this->permissionService->checkPermission($this->cardMapper, $share->getSharedWith(), Acl::PERMISSION_SHARE, $userId, true); - } catch (NoPermissionException $e) { - $permissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE; - } - $share->setPermissions($permissions); - } /** * @inheritDoc */ @@ -652,11 +636,13 @@ private function resolveSharesForRecipient(array $shares, string $userId): array /** @var int[] $ids */ $ids = []; + $cardIds = []; /** @var IShare[] $shareMap */ $shareMap = []; foreach ($shareSlice as $share) { $ids[] = (int)$share->getId(); + $cardIds[] = (int)$share->getSharedWith(); $shareMap[$share->getId()] = $share; } @@ -673,8 +659,22 @@ private function resolveSharesForRecipient(array $shares, string $userId): array $stmt = $query->executeQuery(); + $permissions = $this->permissionService->getPermissionsForCards($cardIds, $userId); + while ($data = $stmt->fetch()) { - $this->applyBoardPermission($shareMap[$data['parent']], (int)$data['permissions'], $userId); + $parent = $shareMap[$data['parent']]; + $sharePermissions = (int)$data['permissions']; + if (isset($permissions[(int)$parent->getSharedWith()])) { + if ($permissions[(int)$parent->getSharedWith()][Acl::PERMISSION_EDIT] === false) { + $sharePermissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE; + $sharePermissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE; + $sharePermissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE; + } + if ($permissions[(int)$parent->getSharedWith()][Acl::PERMISSION_SHARE] === false) { + $sharePermissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE; + } + } + $shareMap[$data['parent']]->setPermissions($sharePermissions); $shareMap[$data['parent']]->setTarget($data['file_target']); } From 66ee9688e7b71566dc196ab1a56215b2f339c97a Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Tue, 20 Oct 2026 10:43:25 +0200 Subject: [PATCH 2/2] fix(sharing): apply owner when getting card permissions Signed-off-by: grnd-alt --- lib/Db/AclMapper.php | 2 +- lib/Service/PermissionService.php | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/Db/AclMapper.php b/lib/Db/AclMapper.php index 7a5a4ef3d3..7c6904ed56 100644 --- a/lib/Db/AclMapper.php +++ b/lib/Db/AclMapper.php @@ -39,7 +39,7 @@ public function findAll(int $boardId, ?int $limit = null, ?int $offset = null) { */ public function findInCards(array $cardIds, ?int $limit = null, ?int $offset = null): array { $qb = $this->db->getQueryBuilder(); - $qb->select('acl.id', 'acl.board_id', 'acl.type', 'acl.participant', 'acl.permission_edit', 'acl.permission_share', 'acl.permission_manage', 'acl.created_at', 'acl.last_modified_at', 'c.id AS card_id') + $qb->select('acl.id', 'acl.board_id', 'acl.type', 'acl.participant', 'acl.permission_edit', 'acl.permission_share', 'acl.permission_manage', 'c.id AS card_id') ->from('deck_board_acl', 'acl') ->innerJoin('acl', 'deck_boards', 'b', 'acl.board_id = b.id') ->innerJoin('b', 'deck_stacks', 's', 's.board_id = b.id') diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php index f25b68a8fc..6212ed3afe 100644 --- a/lib/Service/PermissionService.php +++ b/lib/Service/PermissionService.php @@ -63,11 +63,12 @@ public function getPermissionsForCards(array $cardIds, ?string $userId = null, b $permissions = $this->aclMapper->findInCards($cardIds); return array_map( function (array $acls) use ($userId) { + $owner = $this->userIsBoardOwner($acls[0]->getBoardId(), $userId); return [ - Acl::PERMISSION_READ => $this->userCan($acls, Acl::PERMISSION_READ, $userId), - Acl::PERMISSION_EDIT => $this->userCan($acls, Acl::PERMISSION_EDIT, $userId), - Acl::PERMISSION_MANAGE => $this->userCan($acls, Acl::PERMISSION_MANAGE, $userId), - Acl::PERMISSION_SHARE => $this->userCan($acls, Acl::PERMISSION_SHARE, $userId), + Acl::PERMISSION_READ => $owner || $this->userCan($acls, Acl::PERMISSION_READ, $userId), + Acl::PERMISSION_EDIT => $owner || $this->userCan($acls, Acl::PERMISSION_EDIT, $userId), + Acl::PERMISSION_MANAGE => $owner || $this->userCan($acls, Acl::PERMISSION_MANAGE, $userId), + Acl::PERMISSION_SHARE => ($owner || $this->userCan($acls, Acl::PERMISSION_SHARE, $userId)) && !$this->shareManager->sharingDisabledForUser($userId), ]; }, $permissions