Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/deck.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulius Härtl <jus@bitgrid.net>2022-11-11 15:27:21 +0300
committerGitHub <noreply@github.com>2022-11-11 15:27:21 +0300
commit56935b708a81d3f5cc90e94daee338cec038957a (patch)
treee5522bffe932d0055dfa0d20e3acbd9000f2dd52
parentb42076ccb89a6e8cd279290c0a75c76bb96f54da (diff)
parent2ba8fb9afc79d00842c7a5fa88338372671bb7bc (diff)
Merge pull request #3598 from nextcloud/bugfix/noid/circle-delete
Handle circle deletion event
-rw-r--r--appinfo/info.xml5
-rw-r--r--lib/AppInfo/Application.php65
-rw-r--r--lib/Db/AclMapper.php8
-rw-r--r--lib/Listeners/ParticipantCleanupListener.php57
-rw-r--r--lib/Listeners/ResourceListener.php42
-rw-r--r--lib/Migration/DeletedCircleCleanup.php36
-rw-r--r--tests/psalm-baseline.xml11
-rw-r--r--tests/stub.phpstub6
-rw-r--r--tests/unit/Service/BoardServiceTest.php8
9 files changed, 173 insertions, 65 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml
index d7cd3c84..297db3ef 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -41,6 +41,11 @@
<job>OCA\Deck\Cron\ScheduledNotifications</job>
<job>OCA\Deck\Cron\CardDescriptionActivity</job>
</background-jobs>
+ <repair-steps>
+ <live-migration>
+ <step>OCA\Deck\Migration\DeletedCircleCleanup</step>
+ </live-migration>
+ </repair-steps>
<commands>
<command>OCA\Deck\Command\UserExport</command>
<command>OCA\Deck\Command\BoardImport</command>
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index edbd2b3a..1f973619 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -26,15 +26,13 @@ namespace OCA\Deck\AppInfo;
use Closure;
use Exception;
use OC\EventDispatcher\SymfonyAdapter;
+use OCA\Circles\Events\CircleDestroyedEvent;
use OCA\Deck\Activity\CommentEventHandler;
use OCA\Deck\Capabilities;
use OCA\Deck\Collaboration\Resources\ResourceProvider;
use OCA\Deck\Collaboration\Resources\ResourceProviderCard;
use OCA\Deck\Dashboard\DeckWidget;
use OCA\Deck\Db\Acl;
-use OCA\Deck\Db\AclMapper;
-use OCA\Deck\Db\AssignmentMapper;
-use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Event\AclCreatedEvent;
use OCA\Deck\Event\AclDeletedEvent;
@@ -43,7 +41,9 @@ use OCA\Deck\Event\CardCreatedEvent;
use OCA\Deck\Event\CardDeletedEvent;
use OCA\Deck\Event\CardUpdatedEvent;
use OCA\Deck\Listeners\BeforeTemplateRenderedListener;
+use OCA\Deck\Listeners\ParticipantCleanupListener;
use OCA\Deck\Listeners\FullTextSearchEventListener;
+use OCA\Deck\Listeners\ResourceListener;
use OCA\Deck\Middleware\DefaultBoardMiddleware;
use OCA\Deck\Middleware\ExceptionMiddleware;
use OCA\Deck\Notification\Notifier;
@@ -62,15 +62,12 @@ use OCP\Collaboration\Reference\RenderReferenceEvent;
use OCP\Collaboration\Resources\IProviderManager;
use OCP\Comments\CommentsEntityEvent;
use OCP\Comments\ICommentsManager;
-use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\IConfig;
use OCP\IDBConnection;
-use OCP\IGroupManager;
use OCP\IRequest;
use OCP\Server;
-use OCP\IUserManager;
use OCP\Notification\IManager as NotificationManager;
use OCP\Share\IManager;
use OCP\User\Events\UserDeletedEvent;
@@ -95,7 +92,6 @@ class Application extends App implements IBootstrap {
}
public function boot(IBootContext $context): void {
- $context->injectFn(Closure::fromCallable([$this, 'registerUserGroupHooks']));
$context->injectFn(Closure::fromCallable([$this, 'registerCommentsEntity']));
$context->injectFn(Closure::fromCallable([$this, 'registerCommentsEventHandler']));
$context->injectFn(Closure::fromCallable([$this, 'registerNotifications']));
@@ -143,59 +139,20 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(AclCreatedEvent::class, FullTextSearchEventListener::class);
$context->registerEventListener(AclUpdatedEvent::class, FullTextSearchEventListener::class);
$context->registerEventListener(AclDeletedEvent::class, FullTextSearchEventListener::class);
+
+ // Handling cache invalidation for collections
+ $context->registerEventListener(AclCreatedEvent::class, ResourceListener::class);
+ $context->registerEventListener(AclDeletedEvent::class, ResourceListener::class);
+
+ $context->registerEventListener(UserDeletedEvent::class, ParticipantCleanupListener::class);
+ $context->registerEventListener(GroupDeletedEvent::class, ParticipantCleanupListener::class);
+ $context->registerEventListener(CircleDestroyedEvent::class, ParticipantCleanupListener::class);
}
public function registerNotifications(NotificationManager $notificationManager): void {
$notificationManager->registerNotifierService(Notifier::class);
}
- private function registerUserGroupHooks(IUserManager $userManager, IGroupManager $groupManager): void {
- $container = $this->getContainer();
- /** @var IEventDispatcher $eventDispatcher */
- $eventDispatcher = $container->get(IEventDispatcher::class);
- // Delete user/group acl entries when they get deleted
- $eventDispatcher->addListener(UserDeletedEvent::class, static function (Event $event) use ($container): void {
- if (!($event instanceof UserDeletedEvent)) {
- return;
- }
- $user = $event->getUser();
- // delete existing acl entries for deleted user
- /** @var AclMapper $aclMapper */
- $aclMapper = $container->get(AclMapper::class);
- $acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_USER, $user->getUID());
- foreach ($acls as $acl) {
- $aclMapper->delete($acl);
- }
- // delete existing user assignments
- $assignmentMapper = $container->get(AssignmentMapper::class);
- $assignments = $assignmentMapper->findByParticipant($user->getUID());
- foreach ($assignments as $assignment) {
- $assignmentMapper->delete($assignment);
- }
-
- /** @var BoardMapper $boardMapper */
- $boardMapper = $container->get(BoardMapper::class);
- $boards = $boardMapper->findAllByOwner($user->getUID());
- foreach ($boards as $board) {
- $boardMapper->delete($board);
- }
- });
-
- $eventDispatcher->addListener(GroupDeletedEvent::class, static function (Event $event) use ($container): void {
- if (!($event instanceof GroupDeletedEvent)) {
- return;
- }
- $group = $event->getGroup();
- /** @var AclMapper $aclMapper */
- $aclMapper = $container->get(AclMapper::class);
- $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
- $acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
- foreach ($acls as $acl) {
- $aclMapper->delete($acl);
- }
- });
- }
-
public function registerCommentsEntity(IEventDispatcher $eventDispatcher): void {
$eventDispatcher->addListener(CommentsEntityEvent::EVENT_ENTITY, function (CommentsEntityEvent $event) {
$event->addEntityCollection(self::COMMENT_ENTITY_TYPE, function ($name) {
diff --git a/lib/Db/AclMapper.php b/lib/Db/AclMapper.php
index 4ffbda69..c271d8d3 100644
--- a/lib/Db/AclMapper.php
+++ b/lib/Db/AclMapper.php
@@ -110,4 +110,12 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
->andWhere($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)));
$qb->executeStatement();
}
+
+ public function findByType(int $type): array {
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('*')
+ ->from('deck_board_acl')
+ ->where($qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
+ return $this->findEntities($qb);
+ }
}
diff --git a/lib/Listeners/ParticipantCleanupListener.php b/lib/Listeners/ParticipantCleanupListener.php
new file mode 100644
index 00000000..aa159107
--- /dev/null
+++ b/lib/Listeners/ParticipantCleanupListener.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace OCA\Deck\Listeners;
+
+use OCA\Circles\Events\CircleDestroyedEvent;
+use OCA\Deck\Db\Acl;
+use OCA\Deck\Db\AclMapper;
+use OCA\Deck\Db\AssignmentMapper;
+use OCA\Deck\Db\BoardMapper;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\Group\Events\GroupDeletedEvent;
+use OCP\User\Events\UserDeletedEvent;
+
+class ParticipantCleanupListener implements IEventListener {
+ private AclMapper $aclMapper;
+ private AssignmentMapper $assignmentMapper;
+ private BoardMapper $boardMapper;
+
+ public function __construct(AclMapper $aclMapper, AssignmentMapper $assignmentMapper, BoardMapper $boardMapper) {
+ $this->aclMapper = $aclMapper;
+ $this->assignmentMapper = $assignmentMapper;
+ $this->boardMapper = $boardMapper;
+ }
+
+ public function handle(Event $event): void {
+ if ($event instanceof UserDeletedEvent) {
+ $boards = $this->boardMapper->findAllByOwner($event->getUser()->getUID());
+ foreach ($boards as $board) {
+ $this->boardMapper->delete($board);
+ }
+
+ $this->cleanupByParticipant(Acl::PERMISSION_TYPE_USER, $event->getUser()->getUID());
+ }
+
+ if ($event instanceof GroupDeletedEvent) {
+ $this->cleanupByParticipant(Acl::PERMISSION_TYPE_GROUP, $event->getGroup()->getGID());
+ }
+
+ if ($event instanceof CircleDestroyedEvent) {
+ $circleId = $event->getCircle()->getSingleId();
+ $this->cleanupByParticipant(Acl::PERMISSION_TYPE_CIRCLE, $circleId);
+ }
+ }
+
+ private function cleanupByParticipant(int $type, string $participant): void {
+ $acls = $this->aclMapper->findByParticipant($type, $participant);
+ foreach ($acls as $acl) {
+ $this->aclMapper->delete($acl);
+ }
+
+ $assignments = $this->assignmentMapper->findByParticipant($participant, $type);
+ foreach ($assignments as $assignment) {
+ $this->assignmentMapper->delete($assignment);
+ }
+ }
+}
diff --git a/lib/Listeners/ResourceListener.php b/lib/Listeners/ResourceListener.php
new file mode 100644
index 00000000..08045dff
--- /dev/null
+++ b/lib/Listeners/ResourceListener.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace OCA\Deck\Listeners;
+
+use OCA\Deck\Collaboration\Resources\ResourceProvider;
+use OCA\Deck\Collaboration\Resources\ResourceProviderCard;
+use OCA\Deck\Event\AclCreatedEvent;
+use OCA\Deck\Event\AclDeletedEvent;
+use OCP\Collaboration\Resources\IManager;
+use OCP\Collaboration\Resources\ResourceException;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+
+class ResourceListener implements IEventListener {
+
+ /** @var IManager */
+ private $resourceManager;
+ /** @var ResourceProviderCard */
+ private $resourceProviderCard;
+
+ public function __construct(IManager $resourceManager, ResourceProviderCard $resourceProviderCard) {
+ $this->resourceManager = $resourceManager;
+ $this->resourceProviderCard = $resourceProviderCard;
+ }
+
+ public function handle(Event $event): void {
+ if (!$event instanceof AclDeletedEvent && !$event instanceof AclCreatedEvent) {
+ return;
+ }
+
+ $boardId = $event->getAcl()->getBoardId();
+
+ $this->resourceManager->invalidateAccessCacheForProvider($this->resourceProviderCard);
+
+ try {
+ $resource = $this->resourceManager->getResourceForUser(ResourceProvider::RESOURCE_TYPE, $boardId, null);
+ $this->resourceManager->invalidateAccessCacheForResource($resource);
+ } catch (ResourceException $e) {
+ // If there is no resource we don't need to invalidate anything, but this should not happen anyways
+ }
+ }
+}
diff --git a/lib/Migration/DeletedCircleCleanup.php b/lib/Migration/DeletedCircleCleanup.php
new file mode 100644
index 00000000..e90e80e6
--- /dev/null
+++ b/lib/Migration/DeletedCircleCleanup.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace OCA\Deck\Migration;
+
+use OCA\Deck\Db\Acl;
+use OCA\Deck\Db\AclMapper;
+use OCA\Deck\Service\CirclesService;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class DeletedCircleCleanup implements IRepairStep {
+ private AclMapper $aclMapper;
+ private CirclesService $circleService;
+
+ public function __construct(AclMapper $aclMapper, CirclesService $circlesService) {
+ $this->aclMapper = $aclMapper;
+ $this->circleService = $circlesService;
+ }
+
+ public function getName() {
+ return 'Cleanup Deck ACL entries for circles which have been already deleted';
+ }
+
+ public function run(IOutput $output) {
+ if (!$this->circleService->isCirclesEnabled()) {
+ return;
+ }
+
+ foreach ($this->aclMapper->findByType(Acl::PERMISSION_TYPE_CIRCLE) as $acl) {
+ if ($this->circleService->getCircle($acl->getParticipant()) === null) {
+ $this->aclMapper->delete($acl);
+ $output->info('Removed circle with id ' . $acl->getParticipant());
+ }
+ }
+ }
+}
diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml
index eb6730f9..3bde7e22 100644
--- a/tests/psalm-baseline.xml
+++ b/tests/psalm-baseline.xml
@@ -116,12 +116,6 @@
<code>$schemaClosure</code>
</MoreSpecificImplementedParamType>
</file>
- <file src="lib/Service/AssignmentService.php">
- <InvalidScalarArgument occurrences="2">
- <code>$cardId</code>
- <code>$cardId</code>
- </InvalidScalarArgument>
- </file>
<file src="lib/Service/AttachmentService.php">
<InvalidCatch occurrences="1"/>
</file>
@@ -142,11 +136,6 @@
<code>is_resource($content)</code>
</RedundantCondition>
</file>
- <file src="lib/Service/StackService.php">
- <UndefinedClass occurrences="1">
- <code>BadRquestException</code>
- </UndefinedClass>
- </file>
<file src="lib/Sharing/Listener.php">
<InvalidArgument occurrences="1">
<code>[self::class, 'listenPreShare']</code>
diff --git a/tests/stub.phpstub b/tests/stub.phpstub
index 981250ad..a5ab068e 100644
--- a/tests/stub.phpstub
+++ b/tests/stub.phpstub
@@ -59,6 +59,12 @@ namespace OCA\Circles\Model {
}
}
+namespace OCA\Circles\Events {
+ class CircleDestroyedEvent extends \OCP\EventDispatcher\Event {
+ public function __construct(FederatedEvent $federatedEvent, array $results) {}
+ abstract public function getCircle(): \OCA\Circles\Model\Circle {}
+ }
+}
namespace OCA\Circles\Model\Probes {
class CircleProbe {
public function __construct() {}
diff --git a/tests/unit/Service/BoardServiceTest.php b/tests/unit/Service/BoardServiceTest.php
index 646df29e..96007cc2 100644
--- a/tests/unit/Service/BoardServiceTest.php
+++ b/tests/unit/Service/BoardServiceTest.php
@@ -35,6 +35,8 @@ use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\ChangeHelper;
use OCA\Deck\Db\LabelMapper;
use OCA\Deck\Db\StackMapper;
+use OCA\Deck\Event\AclCreatedEvent;
+use OCA\Deck\Event\AclDeletedEvent;
use OCA\Deck\NoPermissionException;
use OCA\Deck\Notification\NotificationHelper;
use OCP\EventDispatcher\IEventDispatcher;
@@ -375,6 +377,9 @@ class BoardServiceTest extends TestCase {
->method('insert')
->with($acl)
->willReturn($acl);
+ $this->eventDispatcher->expects(self::once())
+ ->method('dispatchTyped')
+ ->with(new AclCreatedEvent($acl));
$this->assertEquals($expected, $this->service->addAcl(
123, 'user', 'admin', $providedAcl[0], $providedAcl[1], $providedAcl[2]
));
@@ -432,6 +437,9 @@ class BoardServiceTest extends TestCase {
->method('delete')
->with($acl)
->willReturn($acl);
+ $this->eventDispatcher->expects(self::once())
+ ->method('dispatchTyped')
+ ->with(new AclDeletedEvent($acl));
$this->assertEquals($acl, $this->service->deleteAcl(123));
}
}