Skip to content

Commit d3fcc71

Browse files
feat: restrict calendar invitation participants
Signed-off-by: SebastianKrupinski <[email protected]>
1 parent 6acf2a6 commit d3fcc71

File tree

4 files changed

+245
-3
lines changed

4 files changed

+245
-3
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@ public function schedule(Message $iTipMessage) {
126126
$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
127127
return;
128128
}
129+
130+
// Check if external attendees are disabled
131+
$externalAttendeesDisabled = $this->config->getValueBool('dav', 'caldav.external_attendees_disabled', false);
132+
if ($externalAttendeesDisabled && !$this->imipService->isSystemUser($recipient)) {
133+
$this->logger->debug('No invitation sent to external attendee (external attendees disabled)', [
134+
'attendee' => $recipient,
135+
]);
136+
$iTipMessage->scheduleStatus = '5.0; External attendees are disabled';
137+
return;
138+
}
139+
129140
$recipientName = $iTipMessage->recipientName ? (string)$iTipMessage->recipientName : null;
130141

131142
$newEvents = $iTipMessage->message;

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

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

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

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

Lines changed: 199 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ public function testDeliveryNoSignificantChange(): void {
130130
$message->senderName = 'Mr. Wizard';
131131
$message->recipient = 'mailto:' . '[email protected]';
132132
$message->significantChange = false;
133+
134+
$this->config->expects(self::never())
135+
->method('getValueBool');
136+
133137
$this->plugin->schedule($message);
134138
$this->assertEquals('1.0', $message->getScheduleStatus());
135139
}
@@ -177,6 +181,12 @@ public function testParsingSingle(): void {
177181
$this->service->expects(self::once())
178182
->method('getLastOccurrence')
179183
->willReturn(1496912700);
184+
$this->config->expects(self::exactly(2))
185+
->method('getValueBool')
186+
->willReturnMap([
187+
['dav', 'caldav.external_attendees_disabled', false, false],
188+
['core', 'mail_providers_enabled', true, false],
189+
]);
180190
$this->eventComparisonService->expects(self::once())
181191
->method('findModified')
182192
->willReturn(['new' => [$newVevent], 'old' => [$oldVEvent]]);
@@ -280,6 +290,10 @@ public function testAttendeeIsResource(): void {
280290
$this->service->expects(self::once())
281291
->method('getLastOccurrence')
282292
->willReturn(1496912700);
293+
$this->config->expects(self::once())
294+
->method('getValueBool')
295+
->with('dav', 'caldav.external_attendees_disabled', false)
296+
->willReturn(false);
283297
$this->eventComparisonService->expects(self::once())
284298
->method('findModified')
285299
->willReturn(['new' => [$newVevent], 'old' => [$oldVEvent]]);
@@ -354,6 +368,10 @@ public function testAttendeeIsCircle(): void {
354368
$this->service->expects(self::once())
355369
->method('getLastOccurrence')
356370
->willReturn(1496912700);
371+
$this->config->expects(self::once())
372+
->method('getValueBool')
373+
->with('dav', 'caldav.external_attendees_disabled', false)
374+
->willReturn(false);
357375
$this->eventComparisonService->expects(self::once())
358376
->method('findModified')
359377
->willReturn(['new' => [$newVevent], 'old' => null]);
@@ -455,6 +473,12 @@ public function testParsingRecurrence(): void {
455473
$this->service->expects(self::once())
456474
->method('getLastOccurrence')
457475
->willReturn(1496912700);
476+
$this->config->expects(self::exactly(2))
477+
->method('getValueBool')
478+
->willReturnMap([
479+
['dav', 'caldav.external_attendees_disabled', false, false],
480+
['core', 'mail_providers_enabled', true, false],
481+
]);
458482
$this->eventComparisonService->expects(self::once())
459483
->method('findModified')
460484
->willReturn(['old' => [] ,'new' => [$newVevent]]);
@@ -695,6 +719,12 @@ public function testMailProviderSend(): void {
695719
$this->service->expects(self::once())
696720
->method('getLastOccurrence')
697721
->willReturn(1496912700);
722+
$this->config->expects(self::exactly(2))
723+
->method('getValueBool')
724+
->willReturnMap([
725+
['dav', 'caldav.external_attendees_disabled', false, false],
726+
['core', 'mail_providers_enabled', true, true],
727+
]);
698728
$this->service->expects(self::once())
699729
->method('getCurrentAttendee')
700730
->with($message)
@@ -837,10 +867,12 @@ public function testMailProviderDisabled(): void {
837867
->method('getValueString')
838868
->with('dav', 'invitation_link_recipients', 'yes')
839869
->willReturn('yes');
840-
$this->config->expects(self::once())
870+
$this->config->expects(self::exactly(2))
841871
->method('getValueBool')
842-
->with('core', 'mail_providers_enabled', true)
843-
->willReturn(false);
872+
->willReturnMap([
873+
['dav', 'caldav.external_attendees_disabled', false, false],
874+
['core', 'mail_providers_enabled', true, false],
875+
]);
844876
$this->service->expects(self::once())
845877
->method('createInvitationToken')
846878
->with($message, $newVevent, 1496912700)
@@ -888,6 +920,12 @@ public function testNoOldEvent(): void {
888920
$this->service->expects(self::once())
889921
->method('getLastOccurrence')
890922
->willReturn(1496912700);
923+
$this->config->expects(self::exactly(2))
924+
->method('getValueBool')
925+
->willReturnMap([
926+
['dav', 'caldav.external_attendees_disabled', false, false],
927+
['core', 'mail_providers_enabled', true, false],
928+
]);
891929
$this->eventComparisonService->expects(self::once())
892930
->method('findModified')
893931
->with($newVCalendar, null)
@@ -981,6 +1019,12 @@ public function testNoButtons(): void {
9811019
$this->service->expects(self::once())
9821020
->method('getLastOccurrence')
9831021
->willReturn(1496912700);
1022+
$this->config->expects(self::exactly(2))
1023+
->method('getValueBool')
1024+
->willReturnMap([
1025+
['dav', 'caldav.external_attendees_disabled', false, false],
1026+
['core', 'mail_providers_enabled', true, false],
1027+
]);
9841028
$this->eventComparisonService->expects(self::once())
9851029
->method('findModified')
9861030
->with($newVCalendar, null)
@@ -1040,4 +1084,156 @@ public function testNoButtons(): void {
10401084
$this->plugin->schedule($message);
10411085
$this->assertEquals('1.1', $message->getScheduleStatus());
10421086
}
1087+
1088+
public function testExternalAttendeesDisabledForExternalUser(): void {
1089+
$message = new Message();
1090+
$message->method = 'REQUEST';
1091+
$newVCalendar = new VCalendar();
1092+
$newVevent = new VEvent($newVCalendar, 'one', array_merge([
1093+
'UID' => 'uid-1234',
1094+
'SEQUENCE' => 1,
1095+
'SUMMARY' => 'Fellowship meeting',
1096+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1097+
], []));
1098+
$newVevent->add('ORGANIZER', 'mailto:[email protected]');
1099+
$newVevent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'External User']);
1100+
$message->message = $newVCalendar;
1101+
$message->sender = 'mailto:[email protected]';
1102+
$message->senderName = 'Mr. Wizard';
1103+
$message->recipient = 'mailto:[email protected]';
1104+
1105+
$this->service->expects(self::once())
1106+
->method('getLastOccurrence')
1107+
->willReturn(1496912700);
1108+
$this->config->expects(self::once())
1109+
->method('getValueBool')
1110+
->with('dav', 'caldav.external_attendees_disabled', false)
1111+
->willReturn(true);
1112+
$this->service->expects(self::once())
1113+
->method('isSystemUser')
1114+
1115+
->willReturn(false);
1116+
$this->eventComparisonService->expects(self::never())
1117+
->method('findModified');
1118+
$this->service->expects(self::never())
1119+
->method('getCurrentAttendee');
1120+
$this->mailer->expects(self::never())
1121+
->method('send');
1122+
1123+
$this->plugin->schedule($message);
1124+
$this->assertEquals('5.0', $message->getScheduleStatus());
1125+
}
1126+
1127+
public function testExternalAttendeesDisabledForSystemUser(): void {
1128+
$message = new Message();
1129+
$message->method = 'REQUEST';
1130+
$newVCalendar = new VCalendar();
1131+
$newVevent = new VEvent($newVCalendar, 'one', array_merge([
1132+
'UID' => 'uid-1234',
1133+
'SEQUENCE' => 1,
1134+
'SUMMARY' => 'Fellowship meeting',
1135+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1136+
], []));
1137+
$newVevent->add('ORGANIZER', 'mailto:[email protected]');
1138+
$newVevent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
1139+
$message->message = $newVCalendar;
1140+
$message->sender = 'mailto:[email protected]';
1141+
$message->senderName = 'Mr. Wizard';
1142+
$message->recipient = 'mailto:[email protected]';
1143+
1144+
$oldVCalendar = new VCalendar();
1145+
$oldVEvent = new VEvent($oldVCalendar, 'one', [
1146+
'UID' => 'uid-1234',
1147+
'SEQUENCE' => 0,
1148+
'SUMMARY' => 'Fellowship meeting',
1149+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1150+
]);
1151+
$oldVEvent->add('ORGANIZER', 'mailto:[email protected]');
1152+
$oldVEvent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
1153+
$oldVCalendar->add($oldVEvent);
1154+
1155+
$data = ['invitee_name' => 'Mr. Wizard',
1156+
'meeting_title' => 'Fellowship meeting',
1157+
'attendee_name' => '[email protected]'
1158+
];
1159+
$attendees = $newVevent->select('ATTENDEE');
1160+
$atnd = '';
1161+
foreach ($attendees as $attendee) {
1162+
if (strcasecmp($attendee->getValue(), $message->recipient) === 0) {
1163+
$atnd = $attendee;
1164+
}
1165+
}
1166+
$this->plugin->setVCalendar($oldVCalendar);
1167+
$this->service->expects(self::once())
1168+
->method('getLastOccurrence')
1169+
->willReturn(1496912700);
1170+
$this->config->expects(self::exactly(2))
1171+
->method('getValueBool')
1172+
->willReturnMap([
1173+
['dav', 'caldav.external_attendees_disabled', false, true],
1174+
['core', 'mail_providers_enabled', true, false],
1175+
]);
1176+
$this->service->expects(self::once())
1177+
->method('isSystemUser')
1178+
1179+
->willReturn(true);
1180+
$this->eventComparisonService->expects(self::once())
1181+
->method('findModified')
1182+
->willReturn(['new' => [$newVevent], 'old' => [$oldVEvent]]);
1183+
$this->service->expects(self::once())
1184+
->method('getCurrentAttendee')
1185+
->with($message)
1186+
->willReturn($atnd);
1187+
$this->service->expects(self::once())
1188+
->method('isRoomOrResource')
1189+
->with($atnd)
1190+
->willReturn(false);
1191+
$this->service->expects(self::once())
1192+
->method('isCircle')
1193+
->with($atnd)
1194+
->willReturn(false);
1195+
$this->service->expects(self::once())
1196+
->method('buildBodyData')
1197+
->with($newVevent, $oldVEvent)
1198+
->willReturn($data);
1199+
$this->user->expects(self::any())
1200+
->method('getUID')
1201+
->willReturn('user1');
1202+
$this->user->expects(self::any())
1203+
->method('getDisplayName')
1204+
->willReturn('Mr. Wizard');
1205+
$this->userSession->expects(self::any())
1206+
->method('getUser')
1207+
->willReturn($this->user);
1208+
$this->service->expects(self::once())
1209+
->method('getFrom');
1210+
$this->service->expects(self::once())
1211+
->method('addSubjectAndHeading')
1212+
->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting', true);
1213+
$this->service->expects(self::once())
1214+
->method('addBulletList')
1215+
->with($this->emailTemplate, $newVevent, $data);
1216+
$this->service->expects(self::once())
1217+
->method('getAttendeeRsvpOrReqForParticipant')
1218+
->willReturn(true);
1219+
$this->config->expects(self::once())
1220+
->method('getValueString')
1221+
->with('dav', 'invitation_link_recipients', 'yes')
1222+
->willReturn('yes');
1223+
$this->service->expects(self::once())
1224+
->method('createInvitationToken')
1225+
->with($message, $newVevent, 1496912700)
1226+
->willReturn('token');
1227+
$this->service->expects(self::once())
1228+
->method('addResponseButtons')
1229+
->with($this->emailTemplate, 'token');
1230+
$this->service->expects(self::once())
1231+
->method('addMoreOptionsButton')
1232+
->with($this->emailTemplate, 'token');
1233+
$this->mailer->expects(self::once())
1234+
->method('send')
1235+
->willReturn([]);
1236+
$this->plugin->schedule($message);
1237+
$this->assertEquals('1.1', $message->getScheduleStatus());
1238+
}
10431239
}

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

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

164+
public function testIsSystemUserWhenUserExists(): void {
165+
$email = '[email protected]';
166+
$user = $this->createMock(\OCP\IUser::class);
167+
168+
$this->userManager->expects(self::once())
169+
->method('getByEmail')
170+
->with($email)
171+
->willReturn([$user]);
172+
173+
$result = $this->service->isSystemUser($email);
174+
$this->assertTrue($result);
175+
}
176+
177+
public function testIsSystemUserWhenUserDoesNotExist(): void {
178+
$email = '[email protected]';
179+
180+
$this->userManager->expects(self::once())
181+
->method('getByEmail')
182+
->with($email)
183+
->willReturn([]);
184+
185+
$result = $this->service->isSystemUser($email);
186+
$this->assertFalse($result);
187+
}
188+
164189
public function testBuildBodyDataCreated(): void {
165190

166191
// construct l10n return(s)

0 commit comments

Comments
 (0)