From a0dfe6542d541540651ec3a480b1bc94ce3e4b29 Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Tue, 17 Dec 2024 16:23:22 -0700 Subject: [PATCH] Tightens up security for calendar subscriptions Signed-off-by: Jon Stovell --- Sources/Actions/Calendar.php | 55 +++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/Sources/Actions/Calendar.php b/Sources/Actions/Calendar.php index 73170292ef..5c0d241f57 100644 --- a/Sources/Actions/Calendar.php +++ b/Sources/Actions/Calendar.php @@ -636,7 +636,7 @@ public function export(): void $file['filename'] = $event->title . '.ics'; $file['mtime'] = $event->modified_time; } else { - $this->authenticateForExport(); + $user = $this->authenticateForExport(); // Get all the visible events within a date range. if (isset($_REQUEST['start_date'])) { @@ -661,7 +661,14 @@ public function export(): void $full_event_uids = []; $tzids = []; - foreach (Event::getOccurrencesInRange($low_date->format('Y-m-d'), $high_date->format('Y-m-d'), true) as $occurrence) { + $query_customizations['where'] = [ + 'cal.start_date <= {date:high_date}', + 'cal.end_date >= {date:low_date}', + 'type = {int:type}', + 'cal.id_board IN (0,' . implode(',', $this->getBoardsForExport($user)) . ')', + ]; + + foreach (Event::getOccurrencesInRange($low_date->format('Y-m-d'), $high_date->format('Y-m-d'), false, $query_customizations) as $occurrence) { $event = $occurrence->getParentEvent(); // Skip if we already exported the full event. @@ -806,7 +813,7 @@ public static function getBirthdayRange(string $low_date, string $high_date): ar $birthdays = []; $high_date = (new \DateTimeImmutable($high_date . ' +1 day'))->format('Y-m-d'); - foreach(Birthday::getOccurrencesInRange($low_date, $high_date) as $occurrence) { + foreach (Birthday::getOccurrencesInRange($low_date, $high_date) as $occurrence) { $birthdays[$occurrence->start->format('Y-m-d')][$occurrence->member] = $occurrence; } @@ -875,7 +882,7 @@ public static function getHolidayRange(string $low_date, string $high_date): arr $holidays = []; $high_date = (new \DateTimeImmutable($high_date . ' +1 day'))->format('Y-m-d'); - foreach(Holiday::getOccurrencesInRange($low_date, $high_date) as $occurrence) { + foreach (Holiday::getOccurrencesInRange($low_date, $high_date) as $occurrence) { $holidays[$occurrence->start->format('Y-m-d')][] = $occurrence; } @@ -1725,23 +1732,45 @@ protected function createToken(User $user): string /** * Validates the guest-supplided user ID and token combination, and loads - * the requested user if the token is valid. + * and returns the requested user if the token is valid. Otherwise, returns + * the current user. * - * Does nothing if the user is already logged in. + * @return SMF\User whose permissions should be used for exporting events. */ - protected function authenticateForExport(): void + protected function authenticateForExport(): User { - if (!User::$me->is_guest) { - return; - } - - if (!empty($_REQUEST['u']) && isset($_REQUEST['token'])) { + if (User::$me->is_guest && !empty($_REQUEST['u']) && isset($_REQUEST['token'])) { $user = current(User::load((int) $_REQUEST['u'])); if (($user instanceof User) && $_REQUEST['token'] === $this->createToken($user)) { - User::setMe($user->id); + return $user; } } + + return User::$me; + } + + /** + * Gets the board IDs for boards where the passed user wants to see events. + * + * @param User $user The user whose permissions should be used. + * @return array An array of board IDs. + */ + protected function getBoardsForExport(User $user): array + { + $request = Db::$db->query( + '', + 'SELECT id_board + FROM {db_prefix}boards + WHERE ' . $user->query_wanna_see_board, + [], + ); + + $board_ids = array_map(fn ($row) => $row['id_board'], Db::$db->fetch_all($request)); + + Db::$db->free_result($request); + + return $board_ids; } }