Skip to content

Commit c2caef5

Browse files
feat: restrict calendar invitation participants
Signed-off-by: SebastianKrupinski <[email protected]>
1 parent 05d6a48 commit c2caef5

File tree

5 files changed

+258
-6
lines changed

5 files changed

+258
-6
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/IMipPluginCharsetTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,12 @@ public function testCharsetMailer(): void {
175175

176176
public function testCharsetMailProvider(): void {
177177
// Arrange
178-
$this->appConfig->method('getValueBool')
179-
->with('core', 'mail_providers_enabled', true)
180-
->willReturn(true);
178+
$this->appConfig->expects(self::exactly(2))
179+
->method('getValueBool')
180+
->willReturnMap([
181+
['dav', 'caldav_external_attendees_disabled', false, false],
182+
['core', 'mail_providers_enabled', true, true],
183+
]);
181184
$mailMessage = new MailProviderMessage();
182185
$mailService = $this->createStubForIntersectionOfInterfaces([IService::class, IMessageSend::class]);
183186
$mailService->method('initiateMessage')

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

Lines changed: 205 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ public function testDeliveryNoSignificantChange(): void {
126126
$message->senderName = 'Mr. Wizard';
127127
$message->recipient = 'mailto:' . '[email protected]';
128128
$message->significantChange = false;
129+
130+
$this->config->expects(self::never())
131+
->method('getValueBool');
132+
129133
$this->plugin->schedule($message);
130134
$this->assertEquals('1.0', $message->getScheduleStatus());
131135
}
@@ -173,6 +177,12 @@ public function testParsingSingle(): void {
173177
$this->service->expects(self::once())
174178
->method('getLastOccurrence')
175179
->willReturn(1496912700);
180+
$this->config->expects(self::exactly(2))
181+
->method('getValueBool')
182+
->willReturnMap([
183+
['dav', 'caldav_external_attendees_disabled', false, false],
184+
['core', 'mail_providers_enabled', true, false],
185+
]);
176186
$this->mailer->expects(self::once())
177187
->method('validateMailAddress')
178188
@@ -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->mailer->expects(self::once())
284298
->method('validateMailAddress')
285299
@@ -358,6 +372,10 @@ public function testAttendeeIsCircle(): void {
358372
$this->service->expects(self::once())
359373
->method('getLastOccurrence')
360374
->willReturn(1496912700);
375+
$this->config->expects(self::once())
376+
->method('getValueBool')
377+
->with('dav', 'caldav_external_attendees_disabled', false)
378+
->willReturn(false);
361379
$this->mailer->expects(self::once())
362380
->method('validateMailAddress')
363381
@@ -463,6 +481,12 @@ public function testParsingRecurrence(): void {
463481
$this->service->expects(self::once())
464482
->method('getLastOccurrence')
465483
->willReturn(1496912700);
484+
$this->config->expects(self::exactly(2))
485+
->method('getValueBool')
486+
->willReturnMap([
487+
['dav', 'caldav_external_attendees_disabled', false, false],
488+
['core', 'mail_providers_enabled', true, false],
489+
]);
466490
$this->mailer->expects(self::once())
467491
->method('validateMailAddress')
468492
@@ -715,6 +739,12 @@ public function testMailProviderSend(): void {
715739
$this->service->expects(self::once())
716740
->method('getLastOccurrence')
717741
->willReturn(1496912700);
742+
$this->config->expects(self::exactly(2))
743+
->method('getValueBool')
744+
->willReturnMap([
745+
['dav', 'caldav_external_attendees_disabled', false, false],
746+
['core', 'mail_providers_enabled', true, true],
747+
]);
718748
$this->service->expects(self::once())
719749
->method('getCurrentAttendee')
720750
->with($message)
@@ -866,10 +896,12 @@ public function testMailProviderDisabled(): void {
866896
->method('getValueString')
867897
->with('dav', 'invitation_link_recipients', 'yes')
868898
->willReturn('yes');
869-
$this->config->expects(self::once())
899+
$this->config->expects(self::exactly(2))
870900
->method('getValueBool')
871-
->with('core', 'mail_providers_enabled', true)
872-
->willReturn(false);
901+
->willReturnMap([
902+
['dav', 'caldav_external_attendees_disabled', false, false],
903+
['core', 'mail_providers_enabled', true, false],
904+
]);
873905
$this->service->expects(self::once())
874906
->method('createInvitationToken')
875907
->with($message, $newVevent, 1496912700)
@@ -917,6 +949,12 @@ public function testNoOldEvent(): void {
917949
$this->service->expects(self::once())
918950
->method('getLastOccurrence')
919951
->willReturn(1496912700);
952+
$this->config->expects(self::exactly(2))
953+
->method('getValueBool')
954+
->willReturnMap([
955+
['dav', 'caldav_external_attendees_disabled', false, false],
956+
['core', 'mail_providers_enabled', true, false],
957+
]);
920958
$this->mailer->expects(self::once())
921959
->method('validateMailAddress')
922960
@@ -1014,6 +1052,12 @@ public function testNoButtons(): void {
10141052
$this->service->expects(self::once())
10151053
->method('getLastOccurrence')
10161054
->willReturn(1496912700);
1055+
$this->config->expects(self::exactly(2))
1056+
->method('getValueBool')
1057+
->willReturnMap([
1058+
['dav', 'caldav_external_attendees_disabled', false, false],
1059+
['core', 'mail_providers_enabled', true, false],
1060+
]);
10171061
$this->mailer->expects(self::once())
10181062
->method('validateMailAddress')
10191063
@@ -1077,4 +1121,162 @@ public function testNoButtons(): void {
10771121
$this->plugin->schedule($message);
10781122
$this->assertEquals('1.1', $message->getScheduleStatus());
10791123
}
1124+
1125+
public function testExternalAttendeesDisabledForExternalUser(): void {
1126+
$message = new Message();
1127+
$message->method = 'REQUEST';
1128+
$newVCalendar = new VCalendar();
1129+
$newVevent = new VEvent($newVCalendar, 'one', array_merge([
1130+
'UID' => 'uid-1234',
1131+
'SEQUENCE' => 1,
1132+
'SUMMARY' => 'Fellowship meeting',
1133+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1134+
], []));
1135+
$newVevent->add('ORGANIZER', 'mailto:[email protected]');
1136+
$newVevent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'External User']);
1137+
$message->message = $newVCalendar;
1138+
$message->sender = 'mailto:[email protected]';
1139+
$message->senderName = 'Mr. Wizard';
1140+
$message->recipient = 'mailto:[email protected]';
1141+
1142+
$this->service->expects(self::once())
1143+
->method('getLastOccurrence')
1144+
->willReturn(1496912700);
1145+
$this->config->expects(self::once())
1146+
->method('getValueBool')
1147+
->with('dav', 'caldav_external_attendees_disabled', false)
1148+
->willReturn(true);
1149+
$this->service->expects(self::once())
1150+
->method('isSystemUser')
1151+
1152+
->willReturn(false);
1153+
$this->eventComparisonService->expects(self::never())
1154+
->method('findModified');
1155+
$this->service->expects(self::never())
1156+
->method('getCurrentAttendee');
1157+
$this->mailer->expects(self::once())
1158+
->method('validateMailAddress')
1159+
->willReturn(true);
1160+
$this->mailer->expects(self::never())
1161+
->method('send');
1162+
1163+
$this->plugin->schedule($message);
1164+
$this->assertEquals('5.0', $message->getScheduleStatus());
1165+
}
1166+
1167+
public function testExternalAttendeesDisabledForSystemUser(): void {
1168+
$message = new Message();
1169+
$message->method = 'REQUEST';
1170+
$newVCalendar = new VCalendar();
1171+
$newVevent = new VEvent($newVCalendar, 'one', array_merge([
1172+
'UID' => 'uid-1234',
1173+
'SEQUENCE' => 1,
1174+
'SUMMARY' => 'Fellowship meeting',
1175+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1176+
], []));
1177+
$newVevent->add('ORGANIZER', 'mailto:[email protected]');
1178+
$newVevent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
1179+
$message->message = $newVCalendar;
1180+
$message->sender = 'mailto:[email protected]';
1181+
$message->senderName = 'Mr. Wizard';
1182+
$message->recipient = 'mailto:[email protected]';
1183+
1184+
$oldVCalendar = new VCalendar();
1185+
$oldVEvent = new VEvent($oldVCalendar, 'one', [
1186+
'UID' => 'uid-1234',
1187+
'SEQUENCE' => 0,
1188+
'SUMMARY' => 'Fellowship meeting',
1189+
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
1190+
]);
1191+
$oldVEvent->add('ORGANIZER', 'mailto:[email protected]');
1192+
$oldVEvent->add('ATTENDEE', 'mailto:[email protected]', ['RSVP' => 'TRUE', 'CN' => 'Frodo']);
1193+
$oldVCalendar->add($oldVEvent);
1194+
1195+
$data = ['invitee_name' => 'Mr. Wizard',
1196+
'meeting_title' => 'Fellowship meeting',
1197+
'attendee_name' => '[email protected]'
1198+
];
1199+
$attendees = $newVevent->select('ATTENDEE');
1200+
$atnd = '';
1201+
foreach ($attendees as $attendee) {
1202+
if (strcasecmp($attendee->getValue(), $message->recipient) === 0) {
1203+
$atnd = $attendee;
1204+
}
1205+
}
1206+
$this->plugin->setVCalendar($oldVCalendar);
1207+
$this->service->expects(self::once())
1208+
->method('getLastOccurrence')
1209+
->willReturn(1496912700);
1210+
$this->config->expects(self::exactly(2))
1211+
->method('getValueBool')
1212+
->willReturnMap([
1213+
['dav', 'caldav_external_attendees_disabled', false, true],
1214+
['core', 'mail_providers_enabled', true, false],
1215+
]);
1216+
$this->service->expects(self::once())
1217+
->method('isSystemUser')
1218+
1219+
->willReturn(true);
1220+
$this->eventComparisonService->expects(self::once())
1221+
->method('findModified')
1222+
->willReturn(['new' => [$newVevent], 'old' => [$oldVEvent]]);
1223+
$this->service->expects(self::once())
1224+
->method('getCurrentAttendee')
1225+
->with($message)
1226+
->willReturn($atnd);
1227+
$this->service->expects(self::once())
1228+
->method('isRoomOrResource')
1229+
->with($atnd)
1230+
->willReturn(false);
1231+
$this->service->expects(self::once())
1232+
->method('isCircle')
1233+
->with($atnd)
1234+
->willReturn(false);
1235+
$this->service->expects(self::once())
1236+
->method('buildBodyData')
1237+
->with($newVevent, $oldVEvent)
1238+
->willReturn($data);
1239+
$this->user->expects(self::any())
1240+
->method('getUID')
1241+
->willReturn('user1');
1242+
$this->user->expects(self::any())
1243+
->method('getDisplayName')
1244+
->willReturn('Mr. Wizard');
1245+
$this->userSession->expects(self::any())
1246+
->method('getUser')
1247+
->willReturn($this->user);
1248+
$this->service->expects(self::once())
1249+
->method('getFrom');
1250+
$this->service->expects(self::once())
1251+
->method('addSubjectAndHeading')
1252+
->with($this->emailTemplate, 'request', 'Mr. Wizard', 'Fellowship meeting', true);
1253+
$this->service->expects(self::once())
1254+
->method('addBulletList')
1255+
->with($this->emailTemplate, $newVevent, $data);
1256+
$this->service->expects(self::once())
1257+
->method('getAttendeeRsvpOrReqForParticipant')
1258+
->willReturn(true);
1259+
$this->config->expects(self::once())
1260+
->method('getValueString')
1261+
->with('dav', 'invitation_link_recipients', 'yes')
1262+
->willReturn('yes');
1263+
$this->service->expects(self::once())
1264+
->method('createInvitationToken')
1265+
->with($message, $newVevent, 1496912700)
1266+
->willReturn('token');
1267+
$this->service->expects(self::once())
1268+
->method('addResponseButtons')
1269+
->with($this->emailTemplate, 'token');
1270+
$this->service->expects(self::once())
1271+
->method('addMoreOptionsButton')
1272+
->with($this->emailTemplate, 'token');
1273+
$this->mailer->expects(self::once())
1274+
->method('validateMailAddress')
1275+
->willReturn(true);
1276+
$this->mailer->expects(self::once())
1277+
->method('send')
1278+
->willReturn([]);
1279+
$this->plugin->schedule($message);
1280+
$this->assertEquals('1.1', $message->getScheduleStatus());
1281+
}
10801282
}

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

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

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

162187
// construct l10n return(s)

0 commit comments

Comments
 (0)