Skip to content

Commit d0f2278

Browse files
authored
Merge pull request #63105 from nextcloud/backport/63004/stable34
[stable34] fix(CalDAV): Check for user status before serving public calendars
2 parents f38836b + e6600ef commit d0f2278

3 files changed

Lines changed: 85 additions & 6 deletions

File tree

apps/dav/lib/CalDAV/PublicCalendarRoot.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
*/
88
namespace OCA\DAV\CalDAV;
99

10+
use OCP\IAppConfig;
1011
use OCP\IConfig;
1112
use OCP\IL10N;
13+
use OCP\IUserManager;
1214
use Psr\Log\LoggerInterface;
1315
use Sabre\DAV\Collection;
16+
use Sabre\DAV\Exception\NotFound;
17+
use Sabre\Uri;
1418

1519
class PublicCalendarRoot extends Collection {
1620

@@ -20,12 +24,15 @@ class PublicCalendarRoot extends Collection {
2024
* @param CalDavBackend $caldavBackend
2125
* @param IL10N $l10n
2226
* @param IConfig $config
27+
* @param IAppConfig $appConfig
2328
*/
2429
public function __construct(
2530
protected CalDavBackend $caldavBackend,
2631
protected IL10N $l10n,
2732
protected IConfig $config,
33+
protected IAppConfig $appConfig,
2834
private LoggerInterface $logger,
35+
private IUserManager $userManager,
2936
) {
3037
}
3138

@@ -43,6 +50,9 @@ public function getName() {
4350
#[\Override]
4451
public function getChild($name) {
4552
$calendar = $this->caldavBackend->getPublicCalendar($name);
53+
if (!$this->validateVisibility((string)$calendar['principaluri'])) {
54+
throw new NotFound('Node with name \'' . $name . '\' could not be found');
55+
}
4656
return new PublicCalendar($this->caldavBackend, $calendar, $this->l10n, $this->config, $this->logger);
4757
}
4858

@@ -53,4 +63,26 @@ public function getChild($name) {
5363
public function getChildren() {
5464
return [];
5565
}
66+
67+
/**
68+
* Checks if the public calendar should be visible or not, based on
69+
* the configuration of the `hide_disabled_user_shares` setting within
70+
* `files_sharing` and the status of the owning user (disabled or not).
71+
*/
72+
private function validateVisibility(string $principalUri): bool {
73+
$hideCalendarsOfDisabledUsers = $this->appConfig->getValueBool(
74+
'files_sharing', 'hide_disabled_user_shares', true
75+
);
76+
77+
if (!$hideCalendarsOfDisabledUsers) {
78+
return true;
79+
}
80+
81+
[$prefix, $name] = Uri\split($principalUri);
82+
if ($prefix !== 'principals/users') {
83+
return true;
84+
}
85+
86+
return $this->userManager->get((string)$name)?->isEnabled() !== false;
87+
}
5688
}

apps/dav/lib/RootCollection.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use OCP\Comments\ICommentsManager;
3737
use OCP\EventDispatcher\IEventDispatcher;
3838
use OCP\Files\IRootFolder;
39+
use OCP\IAppConfig;
3940
use OCP\ICacheFactory;
4041
use OCP\IConfig;
4142
use OCP\IDBConnection;
@@ -62,6 +63,7 @@ public function __construct() {
6263
$db = Server::get(IDBConnection::class);
6364
$dispatcher = Server::get(IEventDispatcher::class);
6465
$config = Server::get(IConfig::class);
66+
$appConfig = Server::get(IAppConfig::class);
6567
$proxyMapper = Server::get(ProxyMapper::class);
6668
$rootFolder = Server::get(IRootFolder::class);
6769
$federatedCalendarFactory = Server::get(FederatedCalendarFactory::class);
@@ -124,7 +126,7 @@ public function __construct() {
124126
$roomCalendarRoot = new CalendarRoot($calendarRoomPrincipalBackend, $caldavBackend, 'principals/calendar-rooms', $logger, $l10n, $config, $federatedCalendarFactory);
125127
$roomCalendarRoot->disableListing = $disableListing;
126128

127-
$publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config, $logger);
129+
$publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config, $appConfig, $logger, $userManager);
128130

129131
$systemTagCollection = Server::get(SystemTagsByIdCollection::class);
130132
$systemTagRelationsCollection = new SystemTagsRelationsCollection(

apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@
1414
use OCA\DAV\CalDAV\PublicCalendarRoot;
1515
use OCA\DAV\Connector\Sabre\Principal;
1616
use OCP\EventDispatcher\IEventDispatcher;
17+
use OCP\IAppConfig;
1718
use OCP\ICacheFactory;
1819
use OCP\IConfig;
1920
use OCP\IDBConnection;
2021
use OCP\IGroupManager;
2122
use OCP\IL10N;
23+
use OCP\IUser;
2224
use OCP\IUserManager;
2325
use OCP\Security\ISecureRandom;
2426
use OCP\Server;
2527
use PHPUnit\Framework\MockObject\MockObject;
2628
use Psr\Log\LoggerInterface;
29+
use Sabre\DAV\Exception\NotFound;
2730
use Test\TestCase;
2831

2932
/**
@@ -35,13 +38,15 @@
3538
#[\PHPUnit\Framework\Attributes\Group(name: 'DB')]
3639
class PublicCalendarRootTest extends TestCase {
3740
public const UNIT_TEST_USER = '';
41+
private const DISABLED_USER_PRINCIPAL = 'principals/users/disabled-caldav-unit-test';
3842
private CalDavBackend $backend;
3943
private PublicCalendarRoot $publicCalendarRoot;
4044
private IL10N&MockObject $l10n;
4145
private Principal&MockObject $principal;
4246
protected IUserManager&MockObject $userManager;
4347
protected IGroupManager&MockObject $groupManager;
4448
protected IConfig&MockObject $config;
49+
protected IAppConfig&MockObject $appConfig;
4550
private ISecureRandom $random;
4651
private LoggerInterface&MockObject $logger;
4752
protected ICacheFactory&MockObject $cacheFactory;
@@ -86,9 +91,10 @@ protected function setUp(): void {
8691
);
8792
$this->l10n = $this->createMock(IL10N::class);
8893
$this->config = $this->createMock(IConfig::class);
94+
$this->appConfig = $this->createMock(IAppConfig::class);
8995

9096
$this->publicCalendarRoot = new PublicCalendarRoot($this->backend,
91-
$this->l10n, $this->config, $this->logger);
97+
$this->l10n, $this->config, $this->appConfig, $this->logger, $this->userManager);
9298
}
9399

94100
protected function tearDown(): void {
@@ -105,7 +111,10 @@ protected function tearDown(): void {
105111
->withAnyParameters()
106112
->willReturn([]);
107113

108-
$books = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
114+
$books = array_merge(
115+
$this->backend->getCalendarsForUser(self::UNIT_TEST_USER),
116+
$this->backend->getCalendarsForUser(self::DISABLED_USER_PRINCIPAL),
117+
);
109118
foreach ($books as $book) {
110119
$this->backend->deleteCalendar($book['id'], true);
111120
}
@@ -135,10 +144,32 @@ public function testGetChildren(): void {
135144
$this->assertSame([], $calendarResults);
136145
}
137146

138-
protected function createPublicCalendar(): Calendar {
139-
$this->backend->createCalendar(self::UNIT_TEST_USER, 'Example', []);
147+
public function testGetChildHidesCalendarOfDisabledUser(): void {
148+
$calendar = $this->createPublicCalendar(self::DISABLED_USER_PRINCIPAL);
149+
$publicUri = $calendar->getPublishStatus();
150+
151+
$this->mockDisabledOwner();
152+
$this->setHideDisabledUserShares(true);
153+
154+
$this->expectException(NotFound::class);
155+
$this->publicCalendarRoot->getChild($publicUri);
156+
}
157+
158+
public function testGetChildServesCalendarOfDisabledUserWhenHidingIsDisabled(): void {
159+
$calendar = $this->createPublicCalendar(self::DISABLED_USER_PRINCIPAL);
160+
$publicUri = $calendar->getPublishStatus();
161+
162+
$this->mockDisabledOwner();
163+
$this->setHideDisabledUserShares(false);
164+
165+
$calendarResult = $this->publicCalendarRoot->getChild($publicUri);
166+
$this->assertEquals($calendar, $calendarResult);
167+
}
168+
169+
protected function createPublicCalendar(string $principal = self::UNIT_TEST_USER): Calendar {
170+
$this->backend->createCalendar($principal, 'Example', []);
140171

141-
$calendarInfo = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER)[0];
172+
$calendarInfo = $this->backend->getCalendarsForUser($principal)[0];
142173
$calendar = new PublicCalendar($this->backend, $calendarInfo, $this->l10n, $this->config, $this->logger);
143174
$publicUri = $calendar->setPublishStatus(true);
144175

@@ -147,4 +178,18 @@ protected function createPublicCalendar(): Calendar {
147178

148179
return $calendar;
149180
}
181+
182+
private function mockDisabledOwner(): void {
183+
$disabledUser = $this->createMock(IUser::class);
184+
$disabledUser->method('isEnabled')
185+
->willReturn(false);
186+
$this->userManager->method('get')
187+
->willReturn($disabledUser);
188+
}
189+
190+
private function setHideDisabledUserShares(bool $hide): void {
191+
$this->appConfig->method('getValueBool')
192+
->with('files_sharing', 'hide_disabled_user_shares', 'yes')
193+
->willReturn($hide);
194+
}
150195
}

0 commit comments

Comments
 (0)