Skip to content
Snippets Groups Projects
Commit 9f17d628 authored by Hangzhi Yu's avatar Hangzhi Yu
Browse files

Check for permissions when accessing lesson events in backend

parent 50ba0341
No related branches found
No related tags found
1 merge request!369Resolve "Permissions for timetables are not checked properly"
Pipeline #192788 passed
......@@ -79,7 +79,9 @@ class TimetableType(Enum):
GROUP = "group"
TEACHER = "teacher"
PARTICIPANT = "participant"
ROOM = "room"
COURSE = "course"
@classmethod
def from_string(cls, s: Optional[str]):
......
# Generated by Django 5.0.7 on 2024-09-13 21:20
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('chronos', '0017_optional_slot_number'),
]
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_lessons_day', 'Can view all lessons per day'), ('view_supervisions_day', 'Can view all supervisions per day'))},
),
]
......@@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from reversion.models import Revision
from aleksis.apps.cursus.models import Course
from aleksis.core.models import Announcement, Group, Person
from aleksis.core.util.core_helpers import get_site_preferences
......@@ -138,7 +139,7 @@ def for_timetables(cls):
Announcement.class_method(for_timetables)
# Dynamically add extra permissions to Group and Person models in core
# Dynamically add extra permissions to Group and Person models in core and Course model in cursus
# Note: requires migrate afterwards
Group.add_permission(
"view_group_timetable",
......@@ -148,6 +149,10 @@ Person.add_permission(
"view_person_timetable",
_("Can view person timetable"),
)
Course.add_permission(
"view_course_timetable",
_("Can view course timetable"),
)
@receiver(timetable_data_changed)
......
......@@ -71,6 +71,7 @@ from aleksis.core.mixins import (
)
from aleksis.core.models import CalendarEvent, Group, Person, Room, SchoolTerm
from aleksis.core.util.core_helpers import get_site_preferences, has_person
from aleksis.core.util.predicates import check_global_permission
class ValidityRange(ExtensibleModel):
......@@ -1293,6 +1294,7 @@ 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_lessons_day", _("Can view all lessons per day")),
("view_supervisions_day", _("Can view all supervisions per day")),
......@@ -1593,6 +1595,20 @@ class LessonEvent(CalendarEvent):
objs = objs.related_to_person(request.user.person)
if type_ and obj_id:
if request and not (
check_global_permission(request.user, "chronos.view_all_group_timetables")
or check_global_permission(request.user, "chronos.view_all_person_timetables")
or check_global_permission(
request.user, "chronos.chronos.view_all_room_timetables"
)
or 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":
return objs.for_teacher(obj_id)
elif type_ == "PARTICIPANT":
......
......@@ -7,6 +7,7 @@ from django.shortcuts import get_object_or_404
from guardian.core import ObjectPermissionChecker
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 aleksis.core.util.predicates import check_global_permission
......@@ -39,10 +40,12 @@ def get_el_by_pk(
Group.objects.prefetch_related("owners", "parent_groups") if prefetch else Group,
pk=pk,
)
elif type_ == TimetableType.TEACHER.value:
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()
......
......@@ -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,24 @@ 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_any_timetable_object(user: User) -> bool:
"""Predicate which checks whether there are any timetables the user is allowed to access."""
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment