mirror of
https://github.com/nextcloud/server.git
synced 2026-06-12 10:10:49 -04:00
Merge pull request #60842 from nextcloud/fix/guard-null-path
fix(files_versions): guard null path in event listeners
This commit is contained in:
commit
d3c74a545a
2 changed files with 109 additions and 0 deletions
|
|
@ -208,6 +208,9 @@ class FileEventsListener implements IEventListener {
|
|||
}
|
||||
|
||||
$path = $this->getPathForNode($node);
|
||||
if ($path === null) {
|
||||
return;
|
||||
}
|
||||
$result = Storage::store($path);
|
||||
|
||||
// Store the result of the version creation so it can be used in post_write_hook.
|
||||
|
|
@ -312,6 +315,9 @@ class FileEventsListener implements IEventListener {
|
|||
$node = $this->versionsDeleted[$path];
|
||||
$relativePath = $this->getPathForNode($node);
|
||||
unset($this->versionsDeleted[$path]);
|
||||
if ($relativePath === null) {
|
||||
return;
|
||||
}
|
||||
Storage::delete($relativePath);
|
||||
// If no new version was stored in the FS, no new version should be added in the DB.
|
||||
// So we simply update the associated version.
|
||||
|
|
@ -325,6 +331,9 @@ class FileEventsListener implements IEventListener {
|
|||
*/
|
||||
public function pre_remove_hook(Node $node): void {
|
||||
$path = $this->getPathForNode($node);
|
||||
if ($path === null) {
|
||||
return;
|
||||
}
|
||||
Storage::markDeletedFile($path);
|
||||
$this->versionsDeleted[$node->getPath()] = $node;
|
||||
}
|
||||
|
|
@ -345,6 +354,9 @@ class FileEventsListener implements IEventListener {
|
|||
|
||||
$oldPath = $this->getPathForNode($source);
|
||||
$newPath = $this->getPathForNode($target);
|
||||
if ($oldPath === null || $newPath === null) {
|
||||
return;
|
||||
}
|
||||
Storage::renameOrCopy($oldPath, $newPath, 'rename');
|
||||
}
|
||||
|
||||
|
|
@ -364,6 +376,9 @@ class FileEventsListener implements IEventListener {
|
|||
|
||||
$oldPath = $this->getPathForNode($source);
|
||||
$newPath = $this->getPathForNode($target);
|
||||
if ($oldPath === null || $newPath === null) {
|
||||
return;
|
||||
}
|
||||
Storage::renameOrCopy($oldPath, $newPath, 'copy');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Tests\Listener;
|
||||
|
||||
use OCA\Files_Versions\Listener\FileEventsListener;
|
||||
use OCA\Files_Versions\Versions\IVersionManager;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\IMimeTypeLoader;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IUserSession;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class FileEventsListenerTest extends TestCase {
|
||||
private IRootFolder&MockObject $rootFolder;
|
||||
private IVersionManager&MockObject $versionManager;
|
||||
private IMimeTypeLoader&MockObject $mimeTypeLoader;
|
||||
private IUserSession&MockObject $userSession;
|
||||
private LoggerInterface&MockObject $logger;
|
||||
private FileEventsListener $listener;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->rootFolder = $this->createMock(IRootFolder::class);
|
||||
$this->versionManager = $this->createMock(IVersionManager::class);
|
||||
$this->mimeTypeLoader = $this->createMock(IMimeTypeLoader::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$this->listener = new FileEventsListener(
|
||||
$this->rootFolder,
|
||||
$this->versionManager,
|
||||
$this->mimeTypeLoader,
|
||||
$this->userSession,
|
||||
$this->logger,
|
||||
);
|
||||
}
|
||||
|
||||
private function createUnresolvableFile(): File&MockObject {
|
||||
$this->userSession->method('getUser')->willReturn(null);
|
||||
|
||||
$node = $this->createMock(File::class);
|
||||
$node->method('getOwner')->willReturn(null);
|
||||
$node->method('getPath')->willReturn('/test.txt');
|
||||
$node->method('getId')->willReturn(42);
|
||||
$node->method('getSize')->willReturn(100);
|
||||
$node->method('getMTime')->willReturn(1234567890);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getPrivateProperty(string $property): mixed {
|
||||
$ref = new \ReflectionProperty(FileEventsListener::class, $property);
|
||||
$ref->setAccessible(true);
|
||||
return $ref->getValue($this->listener);
|
||||
}
|
||||
|
||||
public function testGetPathForNodeReturnsNullWhenUnresolvable(): void {
|
||||
$node = $this->createUnresolvableFile();
|
||||
|
||||
$this->logger->expects($this->once())
|
||||
->method('debug')
|
||||
->with('Failed to compute path for node', $this->anything());
|
||||
|
||||
$method = new \ReflectionMethod(FileEventsListener::class, 'getPathForNode');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertNull($method->invoke($this->listener, $node));
|
||||
}
|
||||
|
||||
public function testWriteHookSkipsWhenPathUnresolvable(): void {
|
||||
$node = $this->createUnresolvableFile();
|
||||
|
||||
$this->listener->write_hook($node);
|
||||
|
||||
$this->assertSame([], $this->getPrivateProperty('writeHookInfo'));
|
||||
}
|
||||
|
||||
public function testPreRemoveHookSkipsWhenPathUnresolvable(): void {
|
||||
$node = $this->createUnresolvableFile();
|
||||
|
||||
$this->listener->pre_remove_hook($node);
|
||||
|
||||
$this->assertSame([], $this->getPrivateProperty('versionsDeleted'));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue