Skip to content

Commit 35a2ff6

Browse files
committed
fix(iMIP): Prevent mails from carrying an unrelated user's name
Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: David Dreschner <david.dreschner@nextcloud.com>
1 parent c50f1e3 commit 35a2ff6

4 files changed

Lines changed: 299 additions & 6 deletions

File tree

apps/dav/lib/CalDAV/Schedule/IMipPlugin.php

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111

1212
use OCA\DAV\CalDAV\CalendarObject;
1313
use OCA\DAV\CalDAV\EventComparisonService;
14+
use OCP\Accounts\IAccountManager;
1415
use OCP\AppFramework\Utility\ITimeFactory;
1516
use OCP\Defaults;
1617
use OCP\IAppConfig;
18+
use OCP\IUser;
19+
use OCP\IUserManager;
1720
use OCP\IUserSession;
1821
use OCP\Mail\IEmailValidator;
1922
use OCP\Mail\IMailer;
@@ -66,6 +69,8 @@ public function __construct(
6669
private EventComparisonService $eventComparisonService,
6770
private IMailManager $mailManager,
6871
private IEmailValidator $emailValidator,
72+
private IUserManager $userManager,
73+
private IAccountManager $accountManager,
6974
) {
7075
parent::__construct('');
7176
}
@@ -184,21 +189,18 @@ public function schedule(Message $iTipMessage) {
184189
}
185190
$this->imipService->setL10nFromAttendee($attendee);
186191

192+
$sender = substr($iTipMessage->sender, 7);
193+
187194
// Build the sender name.
188195
// Due to a bug in sabre, the senderName property for an iTIP message can actually also be a VObject Property
189-
// If the iTIP message senderName is null or empty use the user session name as the senderName
190196
if (($iTipMessage->senderName instanceof Parameter) && !empty(trim($iTipMessage->senderName->getValue()))) {
191197
$senderName = trim($iTipMessage->senderName->getValue());
192198
} elseif (is_string($iTipMessage->senderName) && !empty(trim($iTipMessage->senderName))) {
193199
$senderName = trim($iTipMessage->senderName);
194-
} elseif ($this->userSession->getUser() !== null) {
195-
$senderName = trim($this->userSession->getUser()->getDisplayName());
196200
} else {
197-
$senderName = '';
201+
$senderName = $this->getSenderNameFor($sender);
198202
}
199203

200-
$sender = substr($iTipMessage->sender, 7);
201-
202204
$replyingAttendee = null;
203205
switch (strtolower($iTipMessage->method)) {
204206
case self::METHOD_REPLY:
@@ -338,6 +340,49 @@ public function schedule(Message $iTipMessage) {
338340
}
339341
}
340342

343+
/**
344+
* Resolves a display name for a sender address when the iTip message
345+
* carries no CN parameter.
346+
*
347+
* Messages are regularly brokered on behalf of somebody else, so the
348+
* session user's name is only used when the address is one of theirs.
349+
* Otherwise the address must map unambiguously to a single local user,
350+
* matching how login by email treats ambiguous addresses.
351+
*/
352+
private function getSenderNameFor(string $sender): ?string {
353+
$user = $this->userSession->getUser();
354+
if ($user !== null && $this->isAddressOfUser($sender, $user)) {
355+
return trim($user->getDisplayName());
356+
}
357+
358+
$candidates = $this->userManager->getByEmail($sender);
359+
if (count($candidates) === 1) {
360+
return trim($candidates[0]->getDisplayName());
361+
}
362+
363+
return null;
364+
}
365+
366+
/**
367+
* Whether the address is the user's system email address or one of the
368+
* profile email addresses advertised in their calendar-user-address-set.
369+
*/
370+
private function isAddressOfUser(string $address, IUser $user): bool {
371+
if (strcasecmp((string)$user->getEMailAddress(), $address) === 0) {
372+
return true;
373+
}
374+
375+
$emailCollection = $this->accountManager->getAccount($user)
376+
->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
377+
foreach ($emailCollection->getProperties() as $property) {
378+
if (strcasecmp($property->getValue(), $address) === 0) {
379+
return true;
380+
}
381+
}
382+
383+
return false;
384+
}
385+
341386
/**
342387
* @return ?VCalendar
343388
*/

apps/dav/lib/Server.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ public function __construct(
360360
\OCP\Server::get(EventComparisonService::class),
361361
\OCP\Server::get(\OCP\Mail\Provider\IManager::class),
362362
\OCP\Server::get(IEmailValidator::class),
363+
\OCP\Server::get(\OCP\IUserManager::class),
364+
\OCP\Server::get(IAccountManager::class),
363365
));
364366
}
365367
$this->server->addPlugin(new \OCA\DAV\CalDAV\Search\SearchPlugin());

apps/dav/tests/unit/CalDAV/Schedule/IMipPluginCharsetTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCA\DAV\CalDAV\EventComparisonService;
1414
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
1515
use OCA\DAV\CalDAV\Schedule\IMipService;
16+
use OCP\Accounts\IAccountManager;
1617
use OCP\AppFramework\Utility\ITimeFactory;
1718
use OCP\Config\IUserConfig;
1819
use OCP\Defaults;
@@ -132,6 +133,8 @@ protected function setUp(): void {
132133
$this->eventComparisonService,
133134
$this->mailManager,
134135
$this->getEmailValidatorWithStrictEmailCheck(),
136+
$this->userManager,
137+
$this->createMock(IAccountManager::class),
135138
);
136139

137140
// ITipMessage

0 commit comments

Comments
 (0)