diff --git a/competition/models.py b/competition/models.py index 6a51fa3f..3d88f249 100644 --- a/competition/models.py +++ b/competition/models.py @@ -79,6 +79,10 @@ def can_user_participate(self, user): >= self.min_years_until_graduation return True + @classmethod + def can_user_create(cls, user: User, data): + return user.is_authenticated and user.is_staff + def __str__(self): return self.name @@ -182,6 +186,11 @@ def is_active(self): def can_user_modify(self, user): return self.competition.can_user_modify(user) + @classmethod + def can_user_create(cls, user: User, data: dict) -> bool: + competition = Competition.objects.get(pk=data['competition']) + return competition.can_user_modify(user) + def can_user_participate(self, user): return self.competition.can_user_participate(user) @@ -352,6 +361,11 @@ def num_problems(self) -> int: def can_user_modify(self, user: User) -> bool: return self.semester.can_user_modify(user) + @classmethod + def can_user_create(cls, user: User, data: dict) -> bool: + semester = Semester.objects.get(pk=data['semester']) + return semester.can_user_modify(user) + def can_user_participate(self, user: User) -> bool: return self.semester.can_user_participate(user) @@ -416,6 +430,11 @@ def num_corrected_solutions(self): def can_user_modify(self, user): return self.series.can_user_modify(user) + @classmethod + def can_user_create(cls, user: User, data: dict) -> bool: + series = Series.objects.get(pk=data['series']) + return series.can_user_modify(user) + def get_comments(self, user: User): def filter_by_permissions(obj: 'Comment'): if not user.is_anonymous and obj.can_user_modify(user): @@ -492,6 +511,11 @@ def change_text(self, new_text): def can_user_modify(self, user): return self.problem.can_user_modify(user) + @classmethod + def can_user_create(cls, user: User, data: dict) -> bool: + problem = Problem.objects.get(pk=data['problem']) + return problem.can_user_modify(user) + class Grade(models.Model): """ @@ -571,6 +595,11 @@ def __str__(self): def can_user_modify(self, user): return self.event.can_user_modify(user) + @classmethod + def can_user_create(cls, user: User, data: dict) -> bool: + event = Event.objects.get(pk=data['event']) + return event.can_user_modify(user) + class Vote(models.IntegerChoices): ''' @@ -633,6 +662,11 @@ def get_corrected_solution_file_name(self): def can_user_modify(self, user): return self.problem.can_user_modify(user) + @classmethod + def can_user_create(cls, user: User, data: dict) -> bool: + problem = Problem.objects.get(pk=data['problem']) + return problem.can_user_modify(user) + def set_vote(self, vote): self.vote = vote self.save() @@ -688,6 +722,11 @@ def __str__(self): def can_user_modify(self, user): return self.event.can_user_modify(user) + @classmethod + def can_user_create(cls, user: User, data: dict) -> bool: + event = Event.objects.get(pk=data['event']) + return event.can_user_modify(user) + @receiver(post_save, sender=Publication) def make_name_on_creation(sender, instance, created, **kwargs): diff --git a/competition/permissions.py b/competition/permissions.py index 3928c42e..5dc946f1 100644 --- a/competition/permissions.py +++ b/competition/permissions.py @@ -13,8 +13,8 @@ def has_object_permission(self, request, view, obj): if view.action == 'retrieve': if obj.state == CommentPublishState.PUBLISHED\ - or can_user_modify\ - or obj.posted_by == request.user: + or can_user_modify\ + or obj.posted_by == request.user: return True if view.action in ['publish', 'hide']: @@ -42,6 +42,9 @@ def has_permission(self, request, view): if request.method in permissions.SAFE_METHODS: return True + if request.method == 'POST': + return view.get_serializer().Meta.model.can_user_create(request.user, request.data) + return request.user.is_authenticated and request.user.is_staff def has_object_permission(self, request, view, obj): diff --git a/competition/tests.py b/competition/tests.py index d0701c51..4b41acb0 100644 --- a/competition/tests.py +++ b/competition/tests.py @@ -126,11 +126,21 @@ def test_permission_retrieve(self): def test_permission_update(self): self.check_permissions(self.URL_PREFIX + '/0/', - 'PUT', self.ALL_FORBIDDEN, {'year': 2}) + 'PATCH', self.ONLY_STROM_OK_RESPONSES, + { + "semester": 1, + "order": 1, + "deadline": "2020-01-01T18:00:00Z" + }) def test_permission_create(self): self.check_permissions(self.URL_PREFIX + '/', - 'POST', self.ALL_FORBIDDEN, {'year': 2}) + 'POST', self.ONLY_STROM_OK_RESPONSES, + { + "semester": 1, + "order": 1, + "deadline": "2020-01-01T18:00:00Z" + }) class TestSemester(APITestCase, PermissionTestMixin): @@ -172,9 +182,9 @@ def test_get_semster_result_specific(self): def test_update_permissions(self): ''' update permission OK ''' self.check_permissions(self.URL_PREFIX + '/0/', - 'PUT', self.ALL_FORBIDDEN, {'year': 2}) + 'PATCH', self.ONLY_STROM_OK_RESPONSES, {'year': 2}) self.check_permissions(self.URL_PREFIX + '/10/', - 'PUT', self.ALL_FORBIDDEN, {'year': 2}) + 'PATCH', self.ONLY_KRICKY_OK_RESPONSES, {'year': 2}) def test_public_points_permission(self): self.check_permissions(self.URL_PREFIX + '/', @@ -214,48 +224,6 @@ def setUp(self): def test_create_semester(self): data = { - "series_set": [ - { - "problems": [ - { - "text": "2+2", - "order": 1 - }, - { - "text": "3+3", - "order": 2 - }, - { - "text": "3+4", - "order": 3 - } - ], - "order": 2, - "deadline": "2017-11-19T22:00:00Z", - "complete": False - }, - { - "problems": [ - { - "text": """$EFGH$ je rovnobežník s ostrým uhlom $DAB$. - Dokážte, že $AD$ a $DO$ sú na seba kolmé.""", - "order": 1 - }, - { - "text": """$IJKL$ je rovnobežník s ostrým uhlom $DAB$. - Body $A,\\ P,\\ B,\\ D$ ležia na jednej kružnici v tomto poradí. - Priamky $AP$ a $CD$ sa pretínajú v bode $Q$. Bod $O$ je stred kružnice - opísanej trojuholníku $CPQ$. Dokážte, že ak $D \\neq O$, - tak priamky $AD$ a $DO$ sú na seba kolmé.""", - "order": 2 - } - ], - "order": 2, - "deadline": "2017-11-19T22:00:00Z", - "complete": False - } - ], - "publication_set": [], "year": 42, "school_year": "2017/2018", "start": "2017-10-02T22:00:00Z", @@ -268,8 +236,6 @@ def test_create_semester(self): 'POST', self.ONLY_STROM_OK_RESPONSES, data) self.assertEqual(models.Semester.objects.count(), 2) - self.assertEqual(models.Series.objects.count(), 2) - self.assertEqual(models.Problem.objects.count(), 5) class TestCompetition(APITestCase, PermissionTestMixin): diff --git a/tests/test_utils.py b/tests/test_utils.py index 60e4c151..5061cedc 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -2,6 +2,7 @@ from pathlib import Path from rest_framework.test import APIClient + from user.models import User from webstrom.settings import BASE_DIR @@ -71,7 +72,7 @@ def get_client(self, user_type=None): self.client.force_authenticate(user=user) return self.client - #pylint: disable=dangerous-default-value + # pylint: disable=dangerous-default-value def check_permissions(self, url, method, responses, body={}): for user, status in responses.items(): client = self.get_client(user) @@ -83,6 +84,8 @@ def check_permissions(self, url, method, responses, body={}): response = client.post(url, body, 'json') elif method == 'PUT': response = client.put(url, body, 'json') + elif method == 'PATCH': + response = client.patch(url, body, 'json') else: raise NotImplementedError() self.assertIn(response.status_code, expected_response,