diff --git a/codeforlife/user/permissions/is_independent.py b/codeforlife/user/permissions/is_independent.py index 6e1d0d1d..0873c013 100644 --- a/codeforlife/user/permissions/is_independent.py +++ b/codeforlife/user/permissions/is_independent.py @@ -4,6 +4,8 @@ Created on 12/12/2023 at 13:55:47(+00:00). """ +import typing as t + from ...permissions import IsAuthenticated from ..models import User @@ -11,6 +13,22 @@ class IsIndependent(IsAuthenticated): """Request's user must be independent.""" + def __init__( + self, + is_requesting_to_join_class: t.Optional[bool] = None, + ): + # pylint: disable=line-too-long + """Initialize permission. + + Args: + is_requesting_to_join_class: Check if the independent is (not) + requesting to join a class. If None, don't check. + """ + # pylint: enable=line-too-long + super().__init__() + + self.is_requesting_to_join_class = is_requesting_to_join_class + def has_permission(self, request, view): user = request.user return ( @@ -19,4 +37,15 @@ def has_permission(self, request, view): and user.teacher is None and user.student is not None and user.student.class_field is None + and ( + self.is_requesting_to_join_class is None + or ( + self.is_requesting_to_join_class + and user.student.pending_class_request is not None + ) + or ( + not self.is_requesting_to_join_class + and user.student.pending_class_request is None + ) + ) ) diff --git a/codeforlife/user/views/school.py b/codeforlife/user/views/school.py index f9024f4d..804ddaef 100644 --- a/codeforlife/user/views/school.py +++ b/codeforlife/user/views/school.py @@ -7,7 +7,7 @@ from ...views import ModelViewSet from ..models import School from ..models import User as RequestUser -from ..permissions import IsStudent, IsTeacher +from ..permissions import IsIndependent, IsStudent, IsTeacher from ..serializers import SchoolSerializer @@ -21,12 +21,22 @@ def get_permissions(self): if self.action == "list": return [AllowNone()] - return [OR(IsStudent(), IsTeacher(in_school=True))] + return [ + OR( + OR(IsStudent(), IsTeacher(in_school=True)), + IsIndependent(is_requesting_to_join_class=True), + ) + ] # pylint: disable-next=missing-function-docstring def get_queryset(self): user = self.request.auth_user if user.student: + if user.student.pending_class_request: + return School.objects.filter( + # TODO: should be user.requesting_to_join_class.school_id + id=user.student.pending_class_request.teacher.school_id + ) return School.objects.filter( # TODO: should be user.student.school_id id=user.student.class_field.teacher.school_id diff --git a/codeforlife/user/views/school_test.py b/codeforlife/user/views/school_test.py index 9c59e6c1..e569f51e 100644 --- a/codeforlife/user/views/school_test.py +++ b/codeforlife/user/views/school_test.py @@ -5,8 +5,14 @@ from ...permissions import OR, AllowNone from ...tests import ModelViewSetTestCase -from ..models import School, SchoolTeacherUser, StudentUser, User -from ..permissions import IsStudent, IsTeacher +from ..models import ( + IndependentUser, + School, + SchoolTeacherUser, + StudentUser, + User, +) +from ..permissions import IsIndependent, IsStudent, IsTeacher from ..views import SchoolViewSet RequestUser = User @@ -16,6 +22,7 @@ class TestSchoolViewSet(ModelViewSetTestCase[RequestUser, School]): basename = "school" model_view_set_class = SchoolViewSet + fixtures = ["school_1", "independent"] # test: get permissions @@ -29,7 +36,12 @@ def test_get_permissions__list(self): def test_get_permissions__retrieve(self): """Only student and school-teachers can retrieve a school.""" self.assert_get_permissions( - permissions=[OR(IsStudent(), IsTeacher(in_school=True))], + permissions=[ + OR( + OR(IsStudent(), IsTeacher(in_school=True)), + IsIndependent(is_requesting_to_join_class=True), + ) + ], action="retrieve", ) @@ -55,6 +67,21 @@ def test_get_queryset__student(self): request=self.client.request_factory.get(user=user), ) + def test_get_queryset__independent(self): + """ + An independent-user can only target the school they are requesting + to join. + """ + user = IndependentUser.objects.filter( + new_student__pending_class_request__isnull=False + ).first() + assert user + + self.assert_get_queryset( + values=[user.student.pending_class_request.teacher.school], + request=self.client.request_factory.get(user=user), + ) + # test: actions def test_retrieve(self):