fix(ical): do not expose user email addresses in calendar feeds

The project and user iCal feeds only need a token and they put the
email address of the task assignee and creator in ATTENDEE and
ORGANIZER. Always use the username@kanboard.local address that was
already the fallback for users without an email.

This also fixes ORGANIZER for creators without an email, which was
rendered as a bare MAILTO: because of operator precedence.
This commit is contained in:
dualfroz committed 2026-10-03 15:14:00 +02:00
1 parent 01fa57e17a
commit 6a87749efd
3 files changed
+35 -4

No files matched your search

+2 -2
View File
@@ -133,7 +133,7 @@ class TaskICalFormatter extends BaseFormatter implements FormatterInterface
if (! empty($task['owner_id'])) {
$attendees = new Attendees;
$attendees->add(
'MAILTO:'.($task['assignee_email'] ?: $task['assignee_username'].'@kanboard.local'),
'MAILTO:'.$task['assignee_username'].'@kanboard.local',
array('CN' => $task['assignee_name'] ?: $task['assignee_username'])
);
$vEvent->setAttendees($attendees);
@@ -141,7 +141,7 @@ class TaskICalFormatter extends BaseFormatter implements FormatterInterface
if (! empty($task['creator_id'])) {
$vEvent->setOrganizer(new Organizer(
'MAILTO:' . $task['creator_email'] ?: $task['creator_username'].'@kanboard.local',
'MAILTO:'.$task['creator_username'].'@kanboard.local',
array('CN' => $task['creator_name'] ?: $task['creator_username'])
));
}
-2
View File
@@ -320,10 +320,8 @@ class TaskFinderModel extends Base
->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id')
->columns(
TaskModel::TABLE.'.*',
'ua.email AS assignee_email',
'ua.name AS assignee_name',
'ua.username AS assignee_username',
'uc.email AS creator_email',
'uc.name AS creator_name',
'uc.username AS creator_username'
);
@@ -0,0 +1,33 @@
<?php
namespace KanboardTests\units\Formatter;
use Eluceo\iCal\Component\Calendar;
use Kanboard\Formatter\TaskICalFormatter;
use KanboardTests\units\Base;
class TaskICalFormatterTest extends Base
{
public function testAttendeeAndOrganizerDoNotExposeEmailAddresses()
{
$this->assertEquals(2, $this->container['userModel']->create(array('username' => 'creator', 'name' => 'Task Creator', 'email' => 'creator@example.org')));
$this->assertEquals(3, $this->container['userModel']->create(array('username' => 'assignee', 'name' => 'Task Assignee', 'email' => 'assignee@example.org')));
$this->assertEquals(1, $this->container['projectModel']->create(array('name' => 'P1')));
$this->assertEquals(1, $this->container['taskCreationModel']->create(array(
'project_id' => 1,
'title' => 'Task',
'creator_id' => 2,
'owner_id' => 3,
'date_due' => strtotime('+1 day'),
)));
$calendar = TaskICalFormatter::getInstance($this->container)
->setCalendar(new Calendar('Kanboard'))
->addTasksWithDueDateOnly($this->container['taskFinderModel']->getICalQuery())
->format();
$this->assertStringContainsString('ATTENDEE;CN=Task Assignee:MAILTO:assignee@kanboard.local', $calendar);
$this->assertStringContainsString('ORGANIZER;CN=Task Creator:MAILTO:creator@kanboard.local', $calendar);
$this->assertStringNotContainsString('@example.org', $calendar);
}
}