From af25b8fc7f67e59b1b17262d926e069e2649ef38 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Tue, 31 Oct 2017 11:25:48 +0800 Subject: [PATCH] MDL-60626 calendar: Check capabilities for api All external functions must check all capabilities before returning data. The calendar API itself does not check capabilities (I wish it did), so we must be careful exposing these functions to webservices. --- calendar/externallib.php | 15 ++++++-- calendar/lib.php | 74 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/calendar/externallib.php b/calendar/externallib.php index ce344eef8d1..0ccbf31ae3b 100644 --- a/calendar/externallib.php +++ b/calendar/externallib.php @@ -203,6 +203,8 @@ class core_calendar_external extends external_api { $courses = $params['events']['courseids']; $funcparam['courses'] = $courses; } + // Now get categories we can get events from. + $categories = \coursecat::get_all(); // Let us findout groups that we can return events from. if (!$hassystemcap) { @@ -273,7 +275,8 @@ class core_calendar_external extends external_api { } else { // Can the user actually see this event? $eventobj = calendar_event::load($eventobj); - if (($eventobj->courseid == $SITE->id) || + if ((($eventobj->courseid == $SITE->id) && (empty($eventobj->categoryid))) || + (!empty($eventobj->categoryid) && in_array($eventobj->categoryid, $categories)) || (!empty($eventobj->groupid) && in_array($eventobj->groupid, $groups)) || (!empty($eventobj->courseid) && in_array($eventobj->courseid, $courses)) || ($USER->id == $eventobj->userid) || @@ -723,6 +726,14 @@ class core_calendar_external extends external_api { $warnings = array(); $legacyevent = calendar_event::load($eventid); + // Must check we can see this event. + if (!calendar_view_event_allowed($legacyevent)) { + // We can't return a warning in this case because the event is not optional. + // We don't know the context for the event and it's not worth loading it. + $syscontext = context_system::instance(); + throw new \required_capability_exception($syscontext, 'moodle/course:view', 'nopermission', ''); + } + $legacyevent->count_repeats(); $eventmapper = event_container::get_event_mapper(); @@ -976,7 +987,7 @@ class core_calendar_external extends external_api { $calendar = \calendar_information::create($time, $params['courseid'], $params['categoryid']); self::validate_context($calendar->context); - list($data, $template) = calendar_get_view($calendar, 'day', $params['includenavigation']); + list($data, $template) = calendar_get_view($calendar, 'day'); return $data; } diff --git a/calendar/lib.php b/calendar/lib.php index 78386e2e7e7..2c68e3ece38 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -2113,6 +2113,80 @@ function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) { return array($courses, $group, $user); } +/** + * Return the capability for viewing a calendar event. + * + * @param calendar_event $event event object + * @return boolean + */ +function calendar_view_event_allowed(calendar_event $event) { + global $USER; + + // Anyone can see site events. + if ($event->courseid && $event->courseid == SITEID) { + return true; + } + + // If a user can manage events at the site level they can see any event. + $sitecontext = \context_system::instance(); + // If user has manageentries at site level, return true. + if (has_capability('moodle/calendar:manageentries', $sitecontext)) { + return true; + } + + if (!empty($event->groupid)) { + // If it is a group event we need to be able to manage events in the course, or be in the group. + if (has_capability('moodle/calendar:manageentries', $event->context)) { + return true; + } + + $mycourses = enrol_get_my_courses('id'); + return isset($mycourses[$event->courseid]) && groups_is_member($event->groupid); + } else if ($event->modulename) { + // If this is a module event we need to be able to see the module. + $coursemodules = get_fast_modinfo($event->courseid)->instances; + $hasmodule = isset($coursemodules[$event->modulename]); + $hasinstance = isset($coursemodules[$event->modulename][$event->instance]); + + // If modinfo doesn't know about the module, return false to be safe. + if (!$hasmodule || !$hasinstance) { + return false; + } + + // Must be able to see the course and the module - MDL-59304. + $cm = $coursemodules[$event->modulename][$event->instance]; + if (!$cm->uservisible) { + return false; + } + $mycourses = enrol_get_my_courses('id'); + return isset($mycourses[$event->courseid]); + } else if ($event->categoryid) { + // If this is a category we need to be able to see the category. + $cat = \coursecat::get($event->categoryid, IGNORE_MISSING); + if (!$cat) { + return false; + } + return true; + } else if (!empty($event->courseid)) { + // If it is a course event we need to be able to manage events in the course, or be in the course. + if (has_capability('moodle/calendar:manageentries', $event->context)) { + return true; + } + $mycourses = enrol_get_my_courses('id'); + return isset($mycourses[$event->courseid]); + } else if ($event->userid) { + if ($event->userid != $USER->id) { + // No-one can ever see another users events. + return false; + } + return true; + } else { + throw new moodle_exception('unknown event type'); + } + + return false; +} + /** * Return the capability for editing calendar event. *