Skip to content

Commit 8b73910

Browse files
Move permission checks to view (and tests)
1 parent 650592c commit 8b73910

4 files changed

Lines changed: 268 additions & 234 deletions

File tree

backend/problem/serializers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def create(self, validated_data: dict) -> Problem:
100100
"""
101101
Create a new Problem instance from validated input data.
102102
"""
103-
new_user_problem = Problem(dataset=Problem.Dataset.USER)
103+
new_user_problem = Problem(dataset=Problem.Dataset.USER, extra_data={})
104104

105105
return self._update_core_problem_fields(new_user_problem, validated_data)
106106

backend/problem/serializers_test.py

Lines changed: 5 additions & 219 deletions
Original file line numberDiff line numberDiff line change
@@ -117,76 +117,6 @@ def test_blank_hypothesis_invalid():
117117
assert "hypothesis" in serializer.errors
118118

119119

120-
@pytest.mark.django_db
121-
def test_kb_create_no_permission(user_problem, visitor):
122-
"""Test that KB annotations cannot be created without permission."""
123-
kb_input = [
124-
{
125-
"entity1": "cat",
126-
"entity2": "feline",
127-
"relationship": "equal",
128-
"notes": "Test note",
129-
}
130-
]
131-
132-
serializer = input_serializer_with_user(visitor)
133-
serializer.handle_kb_annotations(user_problem, kb_input) # type: ignore
134-
135-
assert (
136-
KnowledgeBaseAnnotation.objects.filter(problem=user_problem).count() == 0
137-
), "No KB annotations should be created without permission."
138-
139-
140-
@pytest.mark.django_db
141-
def test_kb_update_no_permission(user_problem, kb_annotation, visitor):
142-
"""Test that KB annotations cannot be updated without permission."""
143-
kb_annotation.problem = user_problem
144-
kb_annotation.save()
145-
original_entity1 = kb_annotation.entity1
146-
147-
updated_entity_1 = "updated_entity"
148-
149-
kb_input = [
150-
{
151-
"id": kb_annotation.pk,
152-
"entity1": updated_entity_1,
153-
"entity2": kb_annotation.entity2,
154-
"relationship": kb_annotation.relationship,
155-
}
156-
]
157-
158-
# Preconditions
159-
assert original_entity1 != updated_entity_1
160-
161-
serializer = input_serializer_with_user(visitor)
162-
serializer.handle_kb_annotations(user_problem, kb_input) # type: ignore
163-
164-
# Verify KB annotation was not updated
165-
kb_annotation.refresh_from_db()
166-
assert (
167-
kb_annotation.entity1 == original_entity1
168-
), "KB annotation should not have been modified without permission."
169-
assert updated_entity_1 != original_entity1
170-
171-
172-
@pytest.mark.django_db
173-
def test_kb_mark_removed_no_permission(user_problem, kb_annotation, visitor):
174-
"""Test that KB annotations cannot be marked as removed without permission."""
175-
kb_annotation.problem = user_problem
176-
kb_annotation.save()
177-
178-
kb_input = [] # Empty list should mark any existing KB items as removed.
179-
180-
serializer = input_serializer_with_user(visitor)
181-
serializer.handle_kb_annotations(user_problem, kb_input) # type: ignore
182-
183-
# Verify KB annotation was not removed
184-
kb_annotation.refresh_from_db()
185-
assert (
186-
kb_annotation.removed_at is None
187-
), "KB annotation should not have been marked as removed without permission."
188-
189-
190120
@pytest.mark.django_db
191121
def test_create_single_kb_annotation(user_problem, annotator):
192122
"""Test creating a single KB annotation for a problem."""
@@ -200,7 +130,7 @@ def test_create_single_kb_annotation(user_problem, annotator):
200130
]
201131

202132
serializer = input_serializer_with_user(annotator)
203-
serializer.handle_kb_annotations(user_problem, kb_input)
133+
serializer.handle_kb_annotations(user_problem, kb_input, annotator)
204134

205135
kb_annotations = KnowledgeBaseAnnotation.objects.filter(problem=user_problem)
206136
assert kb_annotations.count() == 1, "One KB annotation should have been created."
@@ -231,7 +161,7 @@ def test_update_single_kb_annotation(user_problem, kb_annotation, annotator):
231161
]
232162

233163
serializer = input_serializer_with_user(annotator)
234-
serializer.handle_kb_annotations(user_problem, kb_input)
164+
serializer.handle_kb_annotations(user_problem, kb_input, annotator)
235165

236166
# Verify KB annotation was updated
237167
kb_annotation.refresh_from_db()
@@ -251,7 +181,7 @@ def test_mark_kb_annotation_as_removed(user_problem, kb_annotation, annotator):
251181
kb_input = [] # Empty list should mark existing as removed
252182

253183
serializer = input_serializer_with_user(annotator)
254-
serializer.handle_kb_annotations(user_problem, kb_input)
184+
serializer.handle_kb_annotations(user_problem, kb_input, annotator)
255185

256186
# Verify KB annotation was marked as removed
257187
kb_annotation.refresh_from_db()
@@ -287,7 +217,7 @@ def test_create_and_update_multiple_kb_annotations(
287217
]
288218

289219
serializer = input_serializer_with_user(annotator)
290-
serializer.handle_kb_annotations(user_problem, kb_input)
220+
serializer.handle_kb_annotations(user_problem, kb_input, annotator)
291221

292222
kb_annotations = KnowledgeBaseAnnotation.objects.filter(
293223
problem=user_problem, removed_at__isnull=True
@@ -337,7 +267,6 @@ def test_create_update_and_remove_kb_annotations(
337267
created_by=annotator,
338268
)
339269

340-
# request = Mock(user=annotator)
341270
kb_input = [
342271
{
343272
"id": kb1.pk,
@@ -353,7 +282,7 @@ def test_create_update_and_remove_kb_annotations(
353282
]
354283

355284
serializer = input_serializer_with_user(annotator)
356-
serializer.handle_kb_annotations(user_problem, kb_input)
285+
serializer.handle_kb_annotations(user_problem, kb_input, annotator)
357286

358287
# Verify kb1 was updated.
359288
kb1.refresh_from_db()
@@ -379,146 +308,3 @@ def test_create_update_and_remove_kb_annotations(
379308
assert new_annotation.entity2 == "new_e2"
380309
assert new_annotation.relationship == "superset"
381310

382-
383-
@pytest.mark.django_db
384-
def test_create_problem_with_kb_annotations(annotator):
385-
"""Test creating a problem with KB annotations through create()."""
386-
data = {
387-
"premises": ["A cat is running."],
388-
"hypothesis": "A cat is moving.",
389-
"kbItems": [
390-
{
391-
"entity1": "cat",
392-
"entity2": "feline",
393-
"relationship": "equal",
394-
"notes": "Test note",
395-
},
396-
{
397-
"entity1": "running",
398-
"entity2": "moving",
399-
"relationship": "subset",
400-
},
401-
],
402-
}
403-
404-
serializer = input_serializer_with_user(annotator, data=data)
405-
assert serializer.is_valid(raise_exception=True)
406-
problem = serializer.save()
407-
408-
# Verify problem was created
409-
assert problem.pk is not None
410-
assert problem.dataset == Problem.Dataset.USER
411-
assert problem.hypothesis.text == "A cat is moving."
412-
assert problem.premises.count() == 1
413-
assert problem.premises.first().text == "A cat is running."
414-
415-
# Verify KB annotations were created
416-
kb_annotations = KnowledgeBaseAnnotation.objects.filter(problem=problem)
417-
assert kb_annotations.count() == 2
418-
419-
kb1 = kb_annotations.get(entity1="cat")
420-
assert kb1.entity2 == "feline"
421-
assert kb1.relationship == "equal"
422-
assert kb1.notes == "Test note"
423-
assert kb1.created_by == annotator
424-
425-
kb2 = kb_annotations.get(entity1="running")
426-
assert kb2.entity2 == "moving"
427-
assert kb2.relationship == "subset"
428-
assert kb2.created_by == annotator
429-
430-
431-
@pytest.mark.django_db
432-
def test_create_problem_with_multiple_premises_and_kb(annotator):
433-
"""Test creating a problem with multiple premises."""
434-
data = {
435-
"premises": ["Birds can fly.", "Penguins are birds."],
436-
"hypothesis": "Penguins can fly.",
437-
}
438-
439-
serializer = input_serializer_with_user(annotator, data=data)
440-
assert serializer.is_valid(raise_exception=True)
441-
problem = serializer.save()
442-
443-
assert problem.premises.count() == 2
444-
premise_texts = [p.text for p in problem.premises.all()]
445-
assert "Birds can fly." in premise_texts
446-
assert "Penguins are birds." in premise_texts
447-
assert problem.hypothesis.text == "Penguins can fly."
448-
449-
450-
@pytest.mark.django_db
451-
def test_update_user_problem_with_new_kb_annotations(user_problem, annotator):
452-
"""Test updating a user problem adds new KB annotations through update()."""
453-
data = {
454-
"id": user_problem.pk,
455-
"premises": ["Updated premise."],
456-
"hypothesis": "Updated hypothesis.",
457-
"kbItems": [
458-
{
459-
"entity1": "new_entity1",
460-
"entity2": "new_entity2",
461-
"relationship": "subset",
462-
}
463-
],
464-
}
465-
466-
assert (
467-
KnowledgeBaseAnnotation.objects.filter(problem=user_problem).count() == 0
468-
), "Precondition: user_problem should have no KB annotations."
469-
470-
serializer = input_serializer_with_user(annotator, data=data, instance=user_problem)
471-
assert serializer.is_valid(raise_exception=True)
472-
updated_problem = serializer.save()
473-
474-
# Verify problem was updated
475-
assert updated_problem.pk == user_problem.pk
476-
assert updated_problem.hypothesis.text == "Updated hypothesis."
477-
assert updated_problem.premises.first().text == "Updated premise."
478-
479-
# Verify KB annotation was added
480-
kb_annotations = KnowledgeBaseAnnotation.objects.filter(problem=updated_problem)
481-
assert kb_annotations.count() == 1
482-
kb_annotation = kb_annotations.first()
483-
assert kb_annotation is not None
484-
assert kb_annotation.entity1 == "new_entity1"
485-
486-
487-
@pytest.mark.django_db
488-
def test_update_non_user_problem_adds_kb_only(non_user_problem, annotator):
489-
"""Test updating a non-user problem only adds KB annotations, not other fields."""
490-
original_hypothesis = non_user_problem.hypothesis.text
491-
original_premise_count = non_user_problem.premises.count()
492-
493-
data = {
494-
"id": non_user_problem.pk,
495-
"premises": ["This should be ignored."],
496-
"hypothesis": "This should also be ignored.",
497-
"kbItems": [
498-
{
499-
"entity1": "entity1",
500-
"entity2": "entity2",
501-
"relationship": "equal",
502-
}
503-
],
504-
}
505-
506-
# Preconditions
507-
assert (
508-
KnowledgeBaseAnnotation.objects.filter(problem=non_user_problem).count() == 0
509-
), "Non_user_problem should have no KB annotations to begin with."
510-
assert original_hypothesis != data["hypothesis"]
511-
512-
serializer = input_serializer_with_user(
513-
annotator, data=data, instance=non_user_problem
514-
)
515-
assert serializer.is_valid(raise_exception=True)
516-
updated_problem = serializer.save()
517-
518-
# Verify problem fields were not updated
519-
assert updated_problem.hypothesis.text == original_hypothesis
520-
assert updated_problem.premises.count() == original_premise_count
521-
522-
# Verify KB annotation was added
523-
kb_annotations = KnowledgeBaseAnnotation.objects.filter(problem=updated_problem)
524-
assert kb_annotations.count() == 1

backend/problem/views/problem.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
from rest_framework.request import Request
33
from rest_framework.response import Response
44
from rest_framework.viewsets import ModelViewSet
5-
from rest_framework.status import HTTP_201_CREATED, HTTP_200_OK, HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
5+
from rest_framework.status import (
6+
HTTP_201_CREATED,
7+
HTTP_200_OK,
8+
HTTP_401_UNAUTHORIZED,
9+
HTTP_403_FORBIDDEN,
10+
)
611
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
712

813
from django.shortcuts import get_object_or_404
@@ -173,13 +178,22 @@ def _handle_update_create_problem(
173178
)
174179
problem = serializer.create(validated_input) # type: ignore
175180
status = HTTP_201_CREATED
181+
182+
elif not (user.can_edit_problem or user.can_edit_kb):
183+
return Response(
184+
{
185+
"detail": "User is not authorized to edit problems or knowledge base annotations."
186+
},
187+
status=HTTP_403_FORBIDDEN,
188+
)
176189

177190
else:
178-
problem_instance = get_object_or_404(Problem, id=problem_id)
179-
problem: Problem = serializer.update(problem_instance, validated_input) if user.can_edit_problem else problem_instance
180-
191+
problem = get_object_or_404(Problem, id=problem_id)
192+
if user.can_edit_problem:
193+
problem: Problem = serializer.update(problem, validated_input) # type: ignore
194+
181195
kb_items = validated_input.get("kbItems", [])
182-
if kb_items:
183-
serializer.handle_kb_annotations(problem=problem, kb_items=kb_items, user=user)
196+
if user.can_edit_kb:
197+
serializer.handle_kb_annotations(problem=problem, kb_items=kb_items, user=user) # type: ignore
184198

185199
return Response({"id": problem.pk}, status=status)

0 commit comments

Comments
 (0)