Skip to content

Commit d21cc34

Browse files
feat: restrict calendar invitation participants
Signed-off-by: SebastianKrupinski <[email protected]> [skip ci]
1 parent a87b956 commit d21cc34

File tree

4 files changed

+214
-3
lines changed

4 files changed

+214
-3
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,18 @@ public function schedule(Message $iTipMessage) {
124124
$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
125125
return;
126126
}
127+
128+
// Check if external attendees are disabled
129+
$externalAttendeesDisabled = $this->config->getValueBool('dav', 'caldav_external_attendees_disabled', false);
130+
if ($externalAttendeesDisabled && !$this->imipService->isSystemUser($recipient)) {
131+
$this->logger->debug('Invitation not sent to external attendee (external attendees disabled)', [
132+
'uid' => $iTipMessage->uid,
133+
'attendee' => $recipient,
134+
]);
135+
$iTipMessage->scheduleStatus = '5.0; External attendees are disabled';
136+
return;
137+
}
138+
127139
$recipientName = $iTipMessage->recipientName ? (string)$iTipMessage->recipientName : null;
128140

129141
$newEvents = $iTipMessage->message;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,16 @@ public function getLastOccurrence(VCalendar $vObject) {
873873
return $dtStart->getDateTime()->getTimeStamp();
874874
}
875875

876+
/**
877+
* Check if an email address belongs to a system user
878+
*
879+
* @param string $email
880+
* @return bool True if the email belongs to a system user, false otherwise
881+
*/
882+
public function isSystemUser(string $email): bool {
883+
return !empty($this->userManager->getByEmail($email));
884+
}
885+
876886
/**
877887
* @param Property $attendee
878888
*/

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

Lines changed: 167 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ public function testDeliveryNoSignificantChange(): void {
156156
$message->senderName = 'Mr. Wizard';
157157
$message->recipient = 'mailto:' . '[email protected]';
158158
$message->significantChange = false;
159+
160+
$this->config->expects(self::never())
161+
->method('getValueBool');
162+
159163
$this->plugin->schedule($message);
160164
$this->assertEquals('1.0', $message->getScheduleStatus());
161165
}
@@ -745,6 +749,12 @@ public function testMailProviderSend(): void {
745749
$this->service->expects(self::once())
746750
->method('getLastOccurrence')
747751
->willReturn(1496912700);
752+
$this->config->expects(self::exactly(2))
753+
->method('getValueBool')
754+
->willReturnMap([
755+
['dav', 'caldav_external_attendees_disabled', false, false],
756+
['core', 'mail_providers_enabled', true, true],
757+
]);
748758
$this->service->expects(self::once())
749759
->method('getCurrentAttendee')
750760
->with($message)
@@ -896,10 +906,12 @@ public function testMailProviderDisabled(): void {
896906
->method('getValueString')
897907
->with('dav', 'invitation_link_recipients', 'yes')
898908
->willReturn('yes');
899-
$this->config->expects(self::once())
909+
$this->config->expects(self::exactly(2))
900910
->method('getValueBool')
901-
->with('core', 'mail_providers_enabled', true)
902-
->willReturn(false);
911+
->willReturnMap([
912+
['dav', 'caldav_external_attendees_disabled', false, false],
913+
['core', 'mail_providers_enabled', true, false],
914+
]);
903915
$this->service->expects(self::once())
904916
->method('createInvitationToken')
905917
->with($message, $newVevent, 1496912700)
@@ -1107,4 +1119,156 @@ public function testNoButtons(): void {
11071119
$this->plugin->schedule($message);
11081120
$this->assertEquals('1.1', $message->getScheduleStatus());
11091121
}
1122+
1123+
public function testExternalAttendeesDisabledForExternalUser(): void {
1124+
$message = new Message();
1125+
$message->method = 'REQUEST';
1126+
$newVCalendar = new VCalendar();
1127+
$newVevent = new VEvent($newVCalendar, 'one', array_merge([
1128+
'UID' => 'uid-1234',
1129+
'SEQUENCE' => 1,
1130+
'SUMMARY' => 'Fellowship meeting',
1131+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1132+
], []));
1133+
$newVevent->add('ORGANIZER', 'mailto:[email protected]');
1134+
$newVevent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'External User']);
1135+
$message->message = $newVCalendar;
1136+
$message->sender = 'mailto:[email protected]';
1137+
$message->senderName = 'Mr. Wizard';
1138+
$message->recipient = 'mailto:[email protected]';
1139+
1140+
$this->service->expects(self::once())
1141+
->method('getLastOccurrence')
1142+
->willReturn(1496912700);
1143+
$this->config->expects(self::once())
1144+
->method('getValueBool')
1145+
->with('dav', 'caldav_external_attendees_disabled', false)
1146+
->willReturn(true);
1147+
$this->service->expects(self::once())
1148+
->method('isSystemUser')
1149+
1150+
->willReturn(false);
1151+
$this->eventComparisonService->expects(self::never())
1152+
->method('findModified');
1153+
$this->service->expects(self::never())
1154+
->method('getCurrentAttendee');
1155+
$this->mailer->expects(self::never())
1156+
->method('send');
1157+
1158+
$this->plugin->schedule($message);
1159+
$this->assertEquals('5.0', $message->getScheduleStatus());
1160+
}
1161+
1162+
public function testExternalAttendeesDisabledForSystemUser(): void {
1163+
$message = new Message();
1164+
$message->method = 'REQUEST';
1165+
$newVCalendar = new VCalendar();
1166+
$newVevent = new VEvent($newVCalendar, 'one', array_merge([
1167+
'UID' => 'uid-1234',
1168+
'SEQUENCE' => 1,
1169+
'SUMMARY' => 'Fellowship meeting',
1170+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1171+
], []));
1172+
$newVevent->add('ORGANIZER', 'mailto:[email protected]');
1173+
$newVevent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
1174+
$message->message = $newVCalendar;
1175+
$message->sender = 'mailto:[email protected]';
1176+
$message->senderName = 'Mr. Wizard';
1177+
$message->recipient = 'mailto:[email protected]';
1178+
1179+
$oldVCalendar = new VCalendar();
1180+
$oldVEvent = new VEvent($oldVCalendar, 'one', [
1181+
'UID' => 'uid-1234',
1182+
'SEQUENCE' => 0,
1183+
'SUMMARY' => 'Fellowship meeting',
1184+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1185+
]);
1186+
$oldVEvent->add('ORGANIZER', 'mailto:[email protected]');
1187+
$oldVEvent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
1188+
$oldVCalendar->add($oldVEvent);
1189+
1190+
$data = ['invitee_name' => 'Mr. Wizard',
1191+
'meeting_title' => 'Fellowship meeting',
1192+
'attendee_name' => '[email protected]'
1193+
];
1194+
$attendees = $newVevent->select('ATTENDEE');
1195+
$atnd = '';
1196+
foreach ($attendees as $attendee) {
1197+
if (strcasecmp($attendee->getValue(), $message->recipient) === 0) {
1198+
$atnd = $attendee;
1199+
}
1200+
}
1201+
$this->plugin->setVCalendar($oldVCalendar);
1202+
$this->service->expects(self::once())
1203+
->method('getLastOccurrence')
1204+
->willReturn(1496912700);
1205+
$this->config->expects(self::exactly(2))
1206+
->method('getValueBool')
1207+
->willReturnMap([
1208+
['dav', 'caldav_external_attendees_disabled', false, true],
1209+
['core', 'mail_providers_enabled', true, false],
1210+
]);
1211+
$this->service->expects(self::once())
1212+
->method('isSystemUser')
1213+
1214+
->willReturn(true);
1215+
$this->eventComparisonService->expects(self::once())
1216+
->method('findModified')
1217+
->willReturn(['new' => [$newVevent], 'old' => [$oldVEvent]]);
1218+
$this->service->expects(self::once())
1219+
->method('getCurrentAttendee')
1220+
->with($message)
1221+
->willReturn($atnd);
1222+
$this->service->expects(self::once())
1223+
->method('isRoomOrResource')
1224+
->with($atnd)
1225+
->willReturn(false);
1226+
$this->service->expects(self::once())
1227+
->method('isCircle')
1228+
->with($atnd)
1229+
->willReturn(false);
1230+
$this->service->expects(self::once())
1231+
->method('buildBodyData')
1232+
->with($newVevent, $oldVEvent)
1233+
->willReturn($data);
1234+
$this->user->expects(self::any())
1235+
->method('getUID')
1236+
->willReturn('user1');
1237+
$this->user->expects(self::any())
1238+
->method('getDisplayName')
1239+
->willReturn('Mr. Wizard');
1240+
$this->userSession->expects(self::any())
1241+
->method('getUser')
1242+
->willReturn($this->user);
1243+
$this->service->expects(self::once())
1244+
->method('getFrom');
1245+
$this->service->expects(self::once())
1246+
->method('addSubjectAndHeading')
1247+
->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting', true);
1248+
$this->service->expects(self::once())
1249+
->method('addBulletList')
1250+
->with($this->emailTemplate, $newVevent, $data);
1251+
$this->service->expects(self::once())
1252+
->method('getAttendeeRsvpOrReqForParticipant')
1253+
->willReturn(true);
1254+
$this->config->expects(self::once())
1255+
->method('getValueString')
1256+
->with('dav', 'invitation_link_recipients', 'yes')
1257+
->willReturn('yes');
1258+
$this->service->expects(self::once())
1259+
->method('createInvitationToken')
1260+
->with($message, $newVevent, 1496912700)
1261+
->willReturn('token');
1262+
$this->service->expects(self::once())
1263+
->method('addResponseButtons')
1264+
->with($this->emailTemplate, 'token');
1265+
$this->service->expects(self::once())
1266+
->method('addMoreOptionsButton')
1267+
->with($this->emailTemplate, 'token');
1268+
$this->mailer->expects(self::once())
1269+
->method('send')
1270+
->willReturn([]);
1271+
$this->plugin->schedule($message);
1272+
$this->assertEquals('1.1', $message->getScheduleStatus());
1273+
}
11101274
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,31 @@ public function testGetFrom(): void {
181181
$this->assertEquals($expected, $actual);
182182
}
183183

184+
public function testIsSystemUserWhenUserExists(): void {
185+
$email = '[email protected]';
186+
$user = $this->createMock(\OCP\IUser::class);
187+
188+
$this->userManager->expects(self::once())
189+
->method('getByEmail')
190+
->with($email)
191+
->willReturn([$user]);
192+
193+
$result = $this->service->isSystemUser($email);
194+
$this->assertTrue($result);
195+
}
196+
197+
public function testIsSystemUserWhenUserDoesNotExist(): void {
198+
$email = '[email protected]';
199+
200+
$this->userManager->expects(self::once())
201+
->method('getByEmail')
202+
->with($email)
203+
->willReturn([]);
204+
205+
$result = $this->service->isSystemUser($email);
206+
$this->assertFalse($result);
207+
}
208+
184209
public function testBuildBodyDataCreated(): void {
185210

186211
// construct l10n return(s)

0 commit comments

Comments
 (0)