diff --git a/aleksis/apps/chronos/managers.py b/aleksis/apps/chronos/managers.py
index 07695bb22e7361f3cbfa02b8d5553d2ad6ed5b5d..9689ef0b167a6706de08e037c1a1a747a6f3a1b7 100644
--- a/aleksis/apps/chronos/managers.py
+++ b/aleksis/apps/chronos/managers.py
@@ -15,7 +15,9 @@ class TimetableType(Enum):
 
     GROUP = "group"
     TEACHER = "teacher"
+    PARTICIPANT = "participant"
     ROOM = "room"
+    COURSE = "course"
 
     @classmethod
     def from_string(cls, s: Optional[str]):
diff --git a/aleksis/apps/chronos/migrations/0020_add_global_permissions.py b/aleksis/apps/chronos/migrations/0020_add_global_permissions.py
new file mode 100644
index 0000000000000000000000000000000000000000..9fbc5102bd4d4368836ed3a8756b000a8c4ec174
--- /dev/null
+++ b/aleksis/apps/chronos/migrations/0020_add_global_permissions.py
@@ -0,0 +1,17 @@
+# Generated by Django 5.0.7 on 2024-09-13 21:20
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('chronos', '0019_remove_old_models'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='chronosglobalpermissions',
+            options={'managed': False, 'permissions': (('view_all_room_timetables', 'Can view all room timetables'), ('view_all_group_timetables', 'Can view all group timetables'), ('view_all_person_timetables', 'Can view all person timetables'), ('view_all_course_timetables', 'Can view all course timetables'), ('view_timetable_overview', 'Can view timetable overview'), ('view_substitutions', 'Can view substitutions table'), ('view_all_room_supervisions', 'Can view all room supervisions'), ('view_all_group_supervisions', 'Can view all group supervisions'), ('view_all_person_supervisions', 'Can view all person supervisions'))},
+        ),
+    ]
diff --git a/aleksis/apps/chronos/model_extensions.py b/aleksis/apps/chronos/model_extensions.py
index 457ef9ef8d0183bf5ddfa5c6799a7e4d14db20d5..7b76336b04e8ea70eb35ca03a0f17f3f1f13f4af 100644
--- a/aleksis/apps/chronos/model_extensions.py
+++ b/aleksis/apps/chronos/model_extensions.py
@@ -1,8 +1,9 @@
 from django.utils.translation import gettext_lazy as _
 
-from aleksis.core.models import Group, Person
+from aleksis.apps.cursus.models import Course
+from aleksis.core.models import Group, Person, Room
 
-# Dynamically add extra permissions to Group and Person models in core
+# Dynamically add permissions to Group, Person and Room models in core and Course model in cursus
 # Note: requires migrate afterwards
 Group.add_permission(
     "view_group_timetable",
@@ -12,3 +13,19 @@ Person.add_permission(
     "view_person_timetable",
     _("Can view person timetable"),
 )
+Group.add_permission(
+    "view_group_supervisions",
+    _("Can view group supervisions"),
+)
+Person.add_permission(
+    "view_person_supervisions",
+    _("Can view person supervisions"),
+)
+Room.add_permission(
+    "view_room_supervisions",
+    _("Can view room supervisions"),
+)
+Course.add_permission(
+    "view_course_timetable",
+    _("Can view course timetable"),
+)
diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index a93a830b36cafd70426cf583334b3ac4ff78302e..f3e7331693b057df03290ebbfd74bfca923e11f8 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -7,6 +7,7 @@ from datetime import date
 from typing import Any
 
 from django.contrib.contenttypes.models import ContentType
+from django.core.exceptions import PermissionDenied
 from django.core.validators import MinValueValidator
 from django.db import models
 from django.db.models import Q, QuerySet
@@ -34,6 +35,7 @@ from aleksis.core.mixins import (
 )
 from aleksis.core.models import CalendarEvent, Group, Person, Room
 from aleksis.core.util.core_helpers import get_site_preferences, has_person
+from aleksis.core.util.predicates import check_global_permission
 
 
 class AutomaticPlan(LiveDocument):
@@ -146,8 +148,12 @@ class ChronosGlobalPermissions(GlobalPermissionModel):
             ("view_all_room_timetables", _("Can view all room timetables")),
             ("view_all_group_timetables", _("Can view all group timetables")),
             ("view_all_person_timetables", _("Can view all person timetables")),
+            ("view_all_course_timetables", _("Can view all course timetables")),
             ("view_timetable_overview", _("Can view timetable overview")),
             ("view_substitutions", _("Can view substitutions table")),
+            ("view_all_room_supervisions", _("Can view all room supervisions")),
+            ("view_all_group_supervisions", _("Can view all group supervisions")),
+            ("view_all_person_supervisions", _("Can view all person supervisions")),
         )
 
 
@@ -444,6 +450,39 @@ class LessonEvent(CalendarEvent):
                     q = q & LessonEventQuerySet.related_to_person_q(request.user.person)
 
             if type_ and obj_id:
+                if request and not (
+                    (
+                        type_ == "GROUP"
+                        and check_global_permission(
+                            request.user, "chronos.view_all_group_timetables"
+                        )
+                    )
+                    or (
+                        type_ == "TEACHER"
+                        or type_ == "PARTICIPANT"
+                        and check_global_permission(
+                            request.user, "chronos.view_all_person_timetables"
+                        )
+                    )
+                    or (
+                        type_ == "ROOM"
+                        and check_global_permission(
+                            request.user, "chronos.view_all_room_timetables"
+                        )
+                    )
+                    or (
+                        type_ == "COURSE"
+                        and check_global_permission(
+                            request.user, "chronos.view_all_course_timetables"
+                        )
+                    )
+                ):
+                    # inline import needed to avoid circular import
+                    from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
+
+                    obj = get_el_by_pk(request, type_.lower(), obj_id)
+                    if not request.user.has_perm("chronos.view_timetable_rule", obj):
+                        raise PermissionDenied()
                 if type_ == "TEACHER":
                     q = q & LessonEventQuerySet.for_teacher_q(obj_id)
                 elif type_ == "PARTICIPANT":
@@ -523,6 +562,32 @@ class SupervisionEvent(LessonEvent):
                 q = q & SupervisionEventQuerySet.amending_q()
 
             if type_ and obj_id:
+                if request and not (
+                    (
+                        type_ == "GROUP"
+                        and check_global_permission(
+                            request.user, "chronos.view_all_group_supervisions"
+                        )
+                    )
+                    or (
+                        type_ == "TEACHER"
+                        and check_global_permission(
+                            request.user, "chronos.view_all_person_supervisions"
+                        )
+                    )
+                    or (
+                        type_ == "ROOM"
+                        and check_global_permission(
+                            request.user, "chronos.view_all_room_supervisions"
+                        )
+                    )
+                ):
+                    # inline import needed to avoid circular import
+                    from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
+
+                    obj = get_el_by_pk(request, type_.lower(), obj_id)
+                    if not request.user.has_perm("chronos.view_supervisions_rule", obj):
+                        raise PermissionDenied()
                 if type_ == "TEACHER":
                     q = q & SupervisionEventQuerySet.for_teacher_q(obj_id)
                 elif type_ == "GROUP":
diff --git a/aleksis/apps/chronos/rules.py b/aleksis/apps/chronos/rules.py
index c7d0065c6bc2c6180a0fa81188484472e6ed8af7..ff5172e781c3500ef40b2f5c96bfcb2e322f9a6a 100644
--- a/aleksis/apps/chronos/rules.py
+++ b/aleksis/apps/chronos/rules.py
@@ -6,7 +6,7 @@ from aleksis.core.util.predicates import (
     has_person,
 )
 
-from .util.predicates import has_any_timetable_object, has_timetable_perm
+from .util.predicates import has_any_timetable_object, has_supervisions_perm, has_timetable_perm
 
 # View timetable overview
 view_timetable_overview_predicate = has_person & (
@@ -18,6 +18,9 @@ add_perm("chronos.view_timetable_overview_rule", view_timetable_overview_predica
 view_timetable_predicate = has_person & has_timetable_perm
 add_perm("chronos.view_timetable_rule", view_timetable_predicate)
 
+# View supervisions for group, person or room
+view_supervisions_predicate = has_person & has_supervisions_perm
+add_perm("chronos.view_supervisions_rule", view_supervisions_predicate)
 
 # Edit substition
 edit_substitution_predicate = has_person & (
diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py
index 961fb01460f8c4fa65504d8726d653d223eeef3b..70688e8e585200a6709be110d9770d64f33e971d 100644
--- a/aleksis/apps/chronos/util/chronos_helpers.py
+++ b/aleksis/apps/chronos/util/chronos_helpers.py
@@ -2,12 +2,16 @@ from datetime import date, datetime, timedelta
 from typing import TYPE_CHECKING, Optional
 
 from django.db.models import Count, Q
+from django.http import HttpRequest, HttpResponseNotFound
+from django.shortcuts import get_object_or_404
 
 from guardian.shortcuts import get_objects_for_user
 
+from aleksis.apps.cursus.models import Course
 from aleksis.core.models import Announcement, Group, Person, Room
 from aleksis.core.util.core_helpers import get_site_preferences
 
+from ..managers import TimetableType
 from .build import build_substitutions_list
 
 if TYPE_CHECKING:
@@ -16,6 +20,29 @@ if TYPE_CHECKING:
     User = get_user_model()  # noqa
 
 
+def get_el_by_pk(
+    request: HttpRequest,
+    type_: str,
+    pk: int,
+    prefetch: bool = False,
+    *args,
+    **kwargs,
+):
+    if type_ == TimetableType.GROUP.value:
+        return get_object_or_404(
+            Group.objects.prefetch_related("owners", "parent_groups") if prefetch else Group,
+            pk=pk,
+        )
+    elif type_ == TimetableType.TEACHER.value or type_ == TimetableType.PARTICIPANT.value:
+        return get_object_or_404(Person, pk=pk)
+    elif type_ == TimetableType.ROOM.value:
+        return get_object_or_404(Room, pk=pk)
+    elif type_ == TimetableType.COURSE.value:
+        return get_object_or_404(Course, pk=pk)
+    else:
+        return HttpResponseNotFound()
+
+
 def get_teachers(user: "User"):
     """Get the teachers whose timetables are allowed to be seen by current user."""
 
diff --git a/aleksis/apps/chronos/util/predicates.py b/aleksis/apps/chronos/util/predicates.py
index 7657ea96becd43be0b92b9ebfda24723b119c44d..b38eec17bdf245ea241363802304859700dcd34a 100644
--- a/aleksis/apps/chronos/util/predicates.py
+++ b/aleksis/apps/chronos/util/predicates.py
@@ -3,6 +3,7 @@ from django.db.models import Model
 
 from rules import predicate
 
+from aleksis.apps.cursus.models import Course
 from aleksis.core.models import Group, Person, Room
 from aleksis.core.util.predicates import has_global_perm, has_object_perm
 
@@ -23,6 +24,8 @@ def has_timetable_perm(user: User, obj: Model) -> bool:
         return has_person_timetable_perm(user, obj)
     elif isinstance(obj, Room):
         return has_room_timetable_perm(user, obj)
+    elif isinstance(obj, Course):
+        return has_course_timetable_perm(user, obj)
     else:
         return False
 
@@ -72,6 +75,88 @@ def has_room_timetable_perm(user: User, obj: Room) -> bool:
     )(user, obj)
 
 
+@predicate
+def has_course_timetable_perm(user: User, obj: Course) -> bool:
+    """
+    Check if can access course timetable.
+
+    Predicate which checks whether the user is allowed
+    to access the requested course timetable.
+    """
+    return (
+        user.person in obj.teachers.all()
+        or obj.groups.all().intersection(user.person.member_of.all()).exists()
+        or user.person.primary_group in obj.groups.all()
+        or obj.groups.all().intersection(user.person.owner_of.all()).exists()
+        or has_global_perm("chronos.view_all_course_timetables")(user)
+        or has_object_perm("cursus.view_course_timetable")(user, obj)
+    )
+
+
+@predicate
+def has_supervisions_perm(user: User, obj: Model) -> bool:
+    """
+    Check if can access supervisions of object.
+
+    Predicate which checks whether the user is allowed
+    to access the requested supervisions of the given
+    group, person or room.
+    """
+    if isinstance(obj, Group):
+        return has_group_supervisions_perm(user, obj)
+    elif isinstance(obj, Person):
+        return has_person_supervisions_perm(user, obj)
+    elif isinstance(obj, Room):
+        return has_room_supervisions_perm(user, obj)
+    else:
+        return False
+
+
+@predicate
+def has_group_supervisions_perm(user: User, obj: Group) -> bool:
+    """
+    Check if can access group supervisions.
+
+    Predicate which checks whether the user is allowed
+    to access the requested group supervisions.
+    """
+    return (
+        obj in user.person.member_of.all()
+        or user.person.primary_group == obj
+        or obj in user.person.owner_of.all()
+        or has_global_perm("chronos.view_all_group_supervisions")(user)
+        or has_object_perm("core.view_group_supervisions")(user, obj)
+    )
+
+
+@predicate
+def has_person_supervisions_perm(user: User, obj: Person) -> bool:
+    """
+    Check if can access person supervisions.
+
+    Predicate which checks whether the user is allowed
+    to access the requested person supervisions.
+    """
+    return (
+        user.person == obj
+        or has_global_perm("chronos.view_all_person_supervisions")(user)
+        or has_object_perm("core.view_person_supervisions")(user, obj)
+    )
+
+
+@predicate
+def has_room_supervisions_perm(user: User, obj: Room) -> bool:
+    """
+    Check if can access room supervisions.
+
+    Predicate which checks whether the user is allowed
+    to access the requested room supervisions.
+    """
+    return has_global_perm("chronos.view_all_room_supervisions")(user) or has_object_perm(
+        "core.view_room_supervisions"
+    )(user, obj)
+
+
 @predicate
 def has_any_timetable_object(user: User) -> bool:
     """Predicate which checks whether there are any timetables the user is allowed to access."""