mirror of
https://github.com/nextcloud/server.git
synced 2026-03-26 20:33:55 -04:00
Merge pull request #54533 from nextcloud/cal-edit-private-event
fix(caldav): show confidential event if writable
This commit is contained in:
commit
a9635044e3
5 changed files with 279 additions and 17 deletions
|
|
@ -53,7 +53,8 @@ class CalendarObject extends \Sabre\CalDAV\CalendarObject {
|
|||
}
|
||||
|
||||
// shows as busy if event is declared confidential
|
||||
if ($this->objectData['classification'] === CalDavBackend::CLASSIFICATION_CONFIDENTIAL) {
|
||||
if ($this->objectData['classification'] === CalDavBackend::CLASSIFICATION_CONFIDENTIAL
|
||||
&& ($this->isPublic() || !$this->canWrite())) {
|
||||
$this->createConfidentialObject($vObject);
|
||||
}
|
||||
|
||||
|
|
@ -135,6 +136,10 @@ class CalendarObject extends \Sabre\CalDAV\CalendarObject {
|
|||
return true;
|
||||
}
|
||||
|
||||
private function isPublic(): bool {
|
||||
return $this->calendarInfo['{http://owncloud.org/ns}public'] ?? false;
|
||||
}
|
||||
|
||||
public function getCalendarId(): int {
|
||||
return (int)$this->objectData['calendarid'];
|
||||
}
|
||||
|
|
|
|||
138
apps/dav/tests/unit/CalDAV/CalendarObjectTest.php
Normal file
138
apps/dav/tests/unit/CalDAV/CalendarObjectTest.php
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Tests\unit\CalDAV;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\CalendarObject;
|
||||
use OCP\IL10N;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
use Sabre\VObject\Component\VEvent;
|
||||
use Sabre\VObject\Reader as VObjectReader;
|
||||
use Test\TestCase;
|
||||
|
||||
class CalendarObjectTest extends TestCase {
|
||||
private readonly CalDavBackend&MockObject $calDavBackend;
|
||||
private readonly IL10N&MockObject $l10n;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->calDavBackend = $this->createMock(CalDavBackend::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
|
||||
$this->l10n->method('t')
|
||||
->willReturnArgument(0);
|
||||
}
|
||||
|
||||
public static function provideConfidentialObjectData(): array {
|
||||
return [
|
||||
// Shared writable
|
||||
[
|
||||
false,
|
||||
[
|
||||
'principaluri' => 'user1',
|
||||
'{http://owncloud.org/ns}owner-principal' => 'user2',
|
||||
],
|
||||
],
|
||||
[
|
||||
false,
|
||||
[
|
||||
'principaluri' => 'user1',
|
||||
'{http://owncloud.org/ns}owner-principal' => 'user2',
|
||||
'{http://owncloud.org/ns}read-only' => 0,
|
||||
],
|
||||
],
|
||||
[
|
||||
false,
|
||||
[
|
||||
'principaluri' => 'user1',
|
||||
'{http://owncloud.org/ns}owner-principal' => 'user2',
|
||||
'{http://owncloud.org/ns}read-only' => false,
|
||||
],
|
||||
],
|
||||
// Shared read-only
|
||||
[
|
||||
true,
|
||||
[
|
||||
'principaluri' => 'user1',
|
||||
'{http://owncloud.org/ns}owner-principal' => 'user2',
|
||||
'{http://owncloud.org/ns}read-only' => 1,
|
||||
],
|
||||
],
|
||||
[
|
||||
true,
|
||||
[
|
||||
'principaluri' => 'user1',
|
||||
'{http://owncloud.org/ns}owner-principal' => 'user2',
|
||||
'{http://owncloud.org/ns}read-only' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideConfidentialObjectData')]
|
||||
public function testGetWithConfidentialObject(
|
||||
bool $expectConfidential,
|
||||
array $calendarInfo,
|
||||
): void {
|
||||
$ics = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
PRODID:-//IDN nextcloud.com//Calendar app 5.5.0-dev.1//EN
|
||||
BEGIN:VEVENT
|
||||
CREATED:20250820T102647Z
|
||||
DTSTAMP:20250820T103038Z
|
||||
LAST-MODIFIED:20250820T103038Z
|
||||
SEQUENCE:4
|
||||
UID:a0f55f1f-4f0e-4db8-a54b-1e8b53846591
|
||||
DTSTART;TZID=Europe/Berlin:20250822T110000
|
||||
DTEND;TZID=Europe/Berlin:20250822T170000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:confidential-event
|
||||
CLASS:CONFIDENTIAL
|
||||
LOCATION:A location
|
||||
DESCRIPTION:A description
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOF;
|
||||
VObjectReader::read($ics);
|
||||
|
||||
$calendarObject = new CalendarObject(
|
||||
$this->calDavBackend,
|
||||
$this->l10n,
|
||||
$calendarInfo,
|
||||
[
|
||||
'uri' => 'a0f55f1f-4f0e-4db8-a54b-1e8b53846591.ics',
|
||||
'calendardata' => $ics,
|
||||
'classification' => 2, // CalDavBackend::CLASSIFICATION_CONFIDENTIAL
|
||||
],
|
||||
);
|
||||
|
||||
$actualIcs = $calendarObject->get();
|
||||
$vObject = VObjectReader::read($actualIcs);
|
||||
|
||||
$this->assertInstanceOf(VCalendar::class, $vObject);
|
||||
$vEvent = $vObject->getBaseComponent('VEVENT');
|
||||
$this->assertInstanceOf(VEvent::class, $vEvent);
|
||||
|
||||
if ($expectConfidential) {
|
||||
$this->assertEquals('Busy', $vEvent->SUMMARY?->getValue());
|
||||
$this->assertNull($vEvent->DESCRIPTION);
|
||||
$this->assertNull($vEvent->LOCATION);
|
||||
} else {
|
||||
$this->assertEquals('confidential-event', $vEvent->SUMMARY?->getValue());
|
||||
$this->assertNotNull($vEvent->DESCRIPTION);
|
||||
$this->assertNotNull($vEvent->LOCATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -297,9 +297,9 @@ class CalendarTest extends TestCase {
|
|||
}
|
||||
$c = new Calendar($backend, $calendarInfo, $this->l10n, $this->config, $this->logger);
|
||||
$children = $c->getChildren();
|
||||
$this->assertEquals($expectedChildren, count($children));
|
||||
$this->assertCount($expectedChildren, $children);
|
||||
$children = $c->getMultipleChildren(['event-0', 'event-1', 'event-2']);
|
||||
$this->assertEquals($expectedChildren, count($children));
|
||||
$this->assertCount($expectedChildren, $children);
|
||||
|
||||
$this->assertEquals(!$isShared, $c->childExists('event-2'));
|
||||
}
|
||||
|
|
@ -375,9 +375,13 @@ EOD;
|
|||
'id' => 666,
|
||||
'uri' => 'cal',
|
||||
];
|
||||
|
||||
if ($isShared) {
|
||||
$calendarInfo['{http://owncloud.org/ns}read-only'] = true;
|
||||
}
|
||||
$c = new Calendar($backend, $calendarInfo, $this->l10n, $this->config, $this->logger);
|
||||
|
||||
$this->assertEquals(count($c->getChildren()), $expectedChildren);
|
||||
$this->assertCount($expectedChildren, $c->getChildren());
|
||||
|
||||
// test private event
|
||||
$privateEvent = $c->getChild('event-1');
|
||||
|
|
@ -582,24 +586,24 @@ EOD;
|
|||
$this->assertCount(2, $roCalendar->getChildren());
|
||||
|
||||
// calendar data shall not be altered for the owner
|
||||
$this->assertEquals($ownerCalendar->getChild('event-0')->get(), $publicObjectData);
|
||||
$this->assertEquals($ownerCalendar->getChild('event-1')->get(), $confidentialObjectData);
|
||||
$this->assertEquals($publicObjectData, $ownerCalendar->getChild('event-0')->get());
|
||||
$this->assertEquals($confidentialObjectData, $ownerCalendar->getChild('event-1')->get());
|
||||
|
||||
// valarms shall not be removed for read-write shares
|
||||
$this->assertEquals(
|
||||
$this->fixLinebreak($rwCalendar->getChild('event-0')->get()),
|
||||
$this->fixLinebreak($publicObjectData));
|
||||
$this->fixLinebreak($publicObjectData),
|
||||
$this->fixLinebreak($rwCalendar->getChild('event-0')->get()));
|
||||
$this->assertEquals(
|
||||
$this->fixLinebreak($rwCalendar->getChild('event-1')->get()),
|
||||
$this->fixLinebreak($confidentialObjectCleaned));
|
||||
$this->fixLinebreak($confidentialObjectData),
|
||||
$this->fixLinebreak($rwCalendar->getChild('event-1')->get()));
|
||||
|
||||
// valarms shall be removed for read-only shares
|
||||
$this->assertEquals(
|
||||
$this->fixLinebreak($roCalendar->getChild('event-0')->get()),
|
||||
$this->fixLinebreak($publicObjectDataWithoutVAlarm));
|
||||
$this->fixLinebreak($publicObjectDataWithoutVAlarm),
|
||||
$this->fixLinebreak($roCalendar->getChild('event-0')->get()));
|
||||
$this->assertEquals(
|
||||
$this->fixLinebreak($roCalendar->getChild('event-1')->get()),
|
||||
$this->fixLinebreak($confidentialObjectCleaned));
|
||||
$this->fixLinebreak($confidentialObjectCleaned),
|
||||
$this->fixLinebreak($roCalendar->getChild('event-1')->get()));
|
||||
}
|
||||
|
||||
private function fixLinebreak(string $str): string {
|
||||
|
|
|
|||
114
apps/dav/tests/unit/CalDAV/PublicCalendarObjectTest.php
Normal file
114
apps/dav/tests/unit/CalDAV/PublicCalendarObjectTest.php
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Tests\unit\CalDAV;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\CalDAV\PublicCalendarObject;
|
||||
use OCP\IL10N;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
use Sabre\VObject\Component\VEvent;
|
||||
use Sabre\VObject\Reader as VObjectReader;
|
||||
use Test\TestCase;
|
||||
|
||||
class PublicCalendarObjectTest extends TestCase {
|
||||
private readonly CalDavBackend&MockObject $calDavBackend;
|
||||
private readonly IL10N&MockObject $l10n;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->calDavBackend = $this->createMock(CalDavBackend::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
|
||||
$this->l10n->method('t')
|
||||
->willReturnArgument(0);
|
||||
}
|
||||
|
||||
public static function provideConfidentialObjectData(): array {
|
||||
// For some reason, the CalDavBackend always sets read-only to false. Hence, we test for
|
||||
// both cases as the property should not matter anyway.
|
||||
// Ref \OCA\DAV\CalDAV\CalDavBackend::getPublicCalendars (approximately in line 538)
|
||||
return [
|
||||
[
|
||||
[
|
||||
'{http://owncloud.org/ns}read-only' => true,
|
||||
'{http://owncloud.org/ns}public' => true,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'{http://owncloud.org/ns}read-only' => false,
|
||||
'{http://owncloud.org/ns}public' => true,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'{http://owncloud.org/ns}read-only' => 1,
|
||||
'{http://owncloud.org/ns}public' => true,
|
||||
],
|
||||
],
|
||||
[
|
||||
[
|
||||
'{http://owncloud.org/ns}read-only' => 0,
|
||||
'{http://owncloud.org/ns}public' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('provideConfidentialObjectData')]
|
||||
public function testGetWithConfidentialObject(array $calendarInfo): void {
|
||||
$ics = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
CALSCALE:GREGORIAN
|
||||
VERSION:2.0
|
||||
PRODID:-//IDN nextcloud.com//Calendar app 5.5.0-dev.1//EN
|
||||
BEGIN:VEVENT
|
||||
CREATED:20250820T102647Z
|
||||
DTSTAMP:20250820T103038Z
|
||||
LAST-MODIFIED:20250820T103038Z
|
||||
SEQUENCE:4
|
||||
UID:a0f55f1f-4f0e-4db8-a54b-1e8b53846591
|
||||
DTSTART;TZID=Europe/Berlin:20250822T110000
|
||||
DTEND;TZID=Europe/Berlin:20250822T170000
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:confidential-event
|
||||
CLASS:CONFIDENTIAL
|
||||
LOCATION:A location
|
||||
DESCRIPTION:A description
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOF;
|
||||
|
||||
$calendarObject = new PublicCalendarObject(
|
||||
$this->calDavBackend,
|
||||
$this->l10n,
|
||||
$calendarInfo,
|
||||
[
|
||||
'uri' => 'a0f55f1f-4f0e-4db8-a54b-1e8b53846591.ics',
|
||||
'calendardata' => $ics,
|
||||
'classification' => 2, // CalDavBackend::CLASSIFICATION_CONFIDENTIAL
|
||||
],
|
||||
);
|
||||
|
||||
$actualIcs = $calendarObject->get();
|
||||
$vObject = VObjectReader::read($actualIcs);
|
||||
|
||||
$this->assertInstanceOf(VCalendar::class, $vObject);
|
||||
$vEvent = $vObject->getBaseComponent('VEVENT');
|
||||
$this->assertInstanceOf(VEvent::class, $vEvent);
|
||||
|
||||
$this->assertEquals('Busy', $vEvent->SUMMARY?->getValue());
|
||||
$this->assertNull($vEvent->DESCRIPTION);
|
||||
$this->assertNull($vEvent->LOCATION);
|
||||
}
|
||||
}
|
||||
|
|
@ -48,9 +48,9 @@ class PublicCalendarTest extends CalendarTest {
|
|||
$logger = $this->createMock(LoggerInterface::class);
|
||||
$c = new PublicCalendar($backend, $calendarInfo, $this->l10n, $config, $logger);
|
||||
$children = $c->getChildren();
|
||||
$this->assertEquals(2, count($children));
|
||||
$this->assertCount(2, $children);
|
||||
$children = $c->getMultipleChildren(['event-0', 'event-1', 'event-2']);
|
||||
$this->assertEquals(2, count($children));
|
||||
$this->assertCount(2, $children);
|
||||
|
||||
$this->assertFalse($c->childExists('event-2'));
|
||||
}
|
||||
|
|
@ -125,6 +125,7 @@ EOD;
|
|||
'principaluri' => 'user2',
|
||||
'id' => 666,
|
||||
'uri' => 'cal',
|
||||
'{http://owncloud.org/ns}public' => true,
|
||||
];
|
||||
/** @var IConfig&MockObject $config */
|
||||
$config = $this->createMock(IConfig::class);
|
||||
|
|
@ -132,7 +133,7 @@ EOD;
|
|||
$logger = $this->createMock(LoggerInterface::class);
|
||||
$c = new PublicCalendar($backend, $calendarInfo, $this->l10n, $config, $logger);
|
||||
|
||||
$this->assertEquals(count($c->getChildren()), 2);
|
||||
$this->assertCount(2, $c->getChildren());
|
||||
|
||||
// test private event
|
||||
$privateEvent = $c->getChild('event-1');
|
||||
|
|
|
|||
Loading…
Reference in a new issue