Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion apps/dav/lib/CalDAV/CalDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -3584,7 +3584,9 @@ private function addOwnerPrincipalToCalendar(array $calendarInfo): array {
$uri = $calendarInfo['principaluri'];
}

$principalInformation = $this->principalBackend->getPrincipalByPath($uri);
$principalInformation = $this->principalBackend->getPrincipalPropertiesByPath($uri, [
'{DAV:}displayname',
]);
if (isset($principalInformation['{DAV:}displayname'])) {
$calendarInfo[$displaynameKey] = $principalInformation['{DAV:}displayname'];
}
Expand Down
23 changes: 23 additions & 0 deletions apps/dav/lib/CalDAV/CalendarRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@

use OCA\DAV\CalDAV\Federation\FederatedCalendarFactory;
use OCA\DAV\CalDAV\Federation\RemoteUserCalendarHome;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\DAV\RemoteUserPrincipalBackend;
use OCP\IConfig;
use OCP\IL10N;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\Backend;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAVACL\PrincipalBackend;

class CalendarRoot extends \Sabre\CalDAV\CalendarRoot {
Expand Down Expand Up @@ -70,4 +72,25 @@ public function getName() {
public function enableReturnCachedSubscriptions(string $principalUri): void {
$this->returnCachedSubscriptions['principals/users/' . $principalUri] = true;
}

public function childExists($name) {
if (!($this->principalBackend instanceof Principal)) {
return parent::childExists($name);
}

// Fetch the most shallow version of the principal just to determine if it exists
$principalInfo = $this->principalBackend->getPrincipalPropertiesByPath(
$this->principalPrefix . '/' . $name,
[],
);
if ($principalInfo === null) {
return false;
}

try {
return $this->getChildForPrincipal($principalInfo) !== null;
} catch (NotFound $e) {
return false;
}
}
}
50 changes: 40 additions & 10 deletions apps/dav/lib/Connector/Sabre/Principal.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ public function getPrincipalsByPrefix($prefixPath) {
* @return array
*/
public function getPrincipalByPath($path) {
return $this->getPrincipalPropertiesByPath($path);
}

/**
* Returns a specific principal, specified by its path.
* The returned structure should be the exact same as from
* getPrincipalsByPrefix.
*
* It is possible to optionally filter retrieved properties in case only a limited set is
* required. Note that the implementation might return more properties than requested.
*
* @param string $path The path of the principal
* @param string[]|null $propertyFilter A list of properties to be retrieved or all if null. An empty array will cause a very shallow principal to be retrieved.
*/
public function getPrincipalPropertiesByPath($path, ?array $propertyFilter = null): ?array {
[$prefix, $name] = \Sabre\Uri\split($path);
$decodedName = urldecode($name);

Expand All @@ -127,7 +142,7 @@ public function getPrincipalByPath($path) {
$user = $this->userManager->get($decodedName);

if ($user !== null) {
return $this->userToPrincipal($user);
return $this->userToPrincipal($user, $propertyFilter);
}
} elseif ($prefix === 'principals/circles') {
if ($this->userSession->getUser() !== null) {
Expand Down Expand Up @@ -466,29 +481,44 @@ public function findByUri($uri, $principalPrefix) {

/**
* @param IUser $user
* @param string[]|null $propertyFilter
* @return array
* @throws PropertyDoesNotExistException
*/
protected function userToPrincipal($user) {
protected function userToPrincipal($user, ?array $propertyFilter = null) {
$wantsProperty = static function (string $name) use ($propertyFilter) {
if ($propertyFilter === null) {
return true;
}

return in_array($name, $propertyFilter, true);
};

$userId = $user->getUID();
$displayName = $user->getDisplayName();
$principal = [
'uri' => $this->principalPrefix . '/' . $userId,
'{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
'{http://nextcloud.com/ns}language' => $this->languageFactory->getUserLanguage($user),
];

$account = $this->accountManager->getAccount($user);
$alternativeEmails = array_map(fn (IAccountProperty $property) => 'mailto:' . $property->getValue(), $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties());
if ($wantsProperty('{http://nextcloud.com/ns}language')) {
$principal['{http://nextcloud.com/ns}language'] = $this->languageFactory->getUserLanguage($user);
}

$email = $user->getSystemEMailAddress();
if (!empty($email)) {
$principal['{http://sabredav.org/ns}email-address'] = $email;
if ($wantsProperty('{http://sabredav.org/ns}email-address')) {
$email = $user->getSystemEMailAddress();
if (!empty($email)) {
$principal['{http://sabredav.org/ns}email-address'] = $email;
}
}

if (!empty($alternativeEmails)) {
$principal['{DAV:}alternate-URI-set'] = $alternativeEmails;
if ($wantsProperty('{DAV:}alternate-URI-set')) {
$account = $this->accountManager->getAccount($user);
$alternativeEmails = array_map(static fn (IAccountProperty $property) => 'mailto:' . $property->getValue(), $account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties());
if (!empty($alternativeEmails)) {
$principal['{DAV:}alternate-URI-set'] = $alternativeEmails;
}
}

return $principal;
Expand Down
17 changes: 13 additions & 4 deletions apps/dav/lib/DAV/Sharing/Backend.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ public function getShares(int $resourceId): array {
$rows = $this->service->getShares($resourceId);
$shares = [];
foreach ($rows as $row) {
$p = $this->getPrincipalByPath($row['principaluri']);
$p = $this->getPrincipalByPath($row['principaluri'], [
'uri',
'{DAV:}displayname',
]);
$shares[] = [
'href' => "principal:{$row['principaluri']}",
'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '',
Expand All @@ -165,7 +168,10 @@ public function preloadShares(array $resourceIds): void {
$sharesByResource = array_fill_keys($resourceIds, []);
foreach ($rows as $row) {
$resourceId = (int)$row['resourceid'];
$p = $this->getPrincipalByPath($row['principaluri']);
$p = $this->getPrincipalByPath($row['principaluri'], [
'uri',
'{DAV:}displayname',
]);
$sharesByResource[$resourceId][] = [
'href' => "principal:{$row['principaluri']}",
'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '',
Expand Down Expand Up @@ -257,12 +263,15 @@ public function getSharesByShareePrincipal(string $principal): array {
return $this->service->getSharesByPrincipals([$principal]);
}

private function getPrincipalByPath(string $principalUri): ?array {
/**
* @param string[]|null $propertyFilter A list of properties to be retrieved or all if null. Is not guaranteed to always be applied and might overfetch.
*/
private function getPrincipalByPath(string $principalUri, ?array $propertyFilter = null): ?array {
// Hacky code below ... shouldn't we check the whole (principal) root collection instead?
if (str_starts_with($principalUri, RemoteUserPrincipalBackend::PRINCIPAL_PREFIX)) {
return $this->remoteUserPrincipalBackend->getPrincipalByPath($principalUri);
}

return $this->principalBackend->getPrincipalByPath($principalUri);
return $this->principalBackend->getPrincipalPropertiesByPath($principalUri, $propertyFilter);
}
}
4 changes: 2 additions & 2 deletions apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ protected function setUp(): void {
$this->createMock(IConfig::class),
$this->createMock(IFactory::class)
])
->onlyMethods(['getPrincipalByPath', 'getGroupMembership', 'findByUri'])
->onlyMethods(['getPrincipalPropertiesByPath', 'getGroupMembership', 'findByUri'])
->getMock();
$this->principal->expects($this->any())->method('getPrincipalByPath')
$this->principal->expects($this->any())->method('getPrincipalPropertiesByPath')
->willReturn([
'uri' => 'principals/best-friend',
'{DAV:}displayname' => 'User\'s displayname',
Expand Down
10 changes: 5 additions & 5 deletions apps/dav/tests/unit/DAV/Sharing/BackendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@ public function testGetShares(): void {
->with($resourceId)
->willReturn($rows);
$this->principalBackend->expects(self::once())
->method('getPrincipalByPath')
->with($principal)
->method('getPrincipalPropertiesByPath')
->with($principal, ['uri', '{DAV:}displayname'])
->willReturn(['uri' => $principal, '{DAV:}displayname' => 'bob']);
$this->shareCache->expects(self::once())
->method('set')
Expand Down Expand Up @@ -354,8 +354,8 @@ public function testGetSharesAddressbooks(): void {
->with($resourceId)
->willReturn($rows);
$this->principalBackend->expects(self::once())
->method('getPrincipalByPath')
->with($principal)
->method('getPrincipalPropertiesByPath')
->with($principal, ['uri', '{DAV:}displayname'])
->willReturn(['uri' => $principal, '{DAV:}displayname' => 'bob']);
$this->shareCache->expects(self::once())
->method('set')
Expand Down Expand Up @@ -392,7 +392,7 @@ public function testPreloadShares(): void {
->with($resourceIds)
->willReturn($rows);
$this->principalBackend->expects(self::exactly(2))
->method('getPrincipalByPath')
->method('getPrincipalPropertiesByPath')
->willReturnCallback(function (string $principal) use ($principalResults) {
switch ($principal) {
case 'principals/groups/bob':
Expand Down
7 changes: 0 additions & 7 deletions build/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -740,8 +740,6 @@
<code><![CDATA[$results]]></code>
</InvalidScalarArgument>
<NullableReturnStatement>
<code><![CDATA[$this->circleToPrincipal($decodedName)
?: $this->circleToPrincipal($name)]]></code>
<code><![CDATA[null]]></code>
<code><![CDATA[null]]></code>
<code><![CDATA[null]]></code>
Expand Down Expand Up @@ -832,11 +830,6 @@
<code><![CDATA[null]]></code>
</NullableReturnStatement>
</file>
<file src="apps/dav/lib/DAV/Sharing/Backend.php">
<LessSpecificReturnType>
<code><![CDATA[?array]]></code>
</LessSpecificReturnType>
</file>
<file src="apps/dav/lib/DAV/Sharing/Plugin.php">
<DeprecatedMethod>
<code><![CDATA[getAppValue]]></code>
Expand Down
Loading