Adjust permissions, add unit tests

This commit is contained in:
Ramon Wenger 2022-07-06 17:48:29 +02:00
parent 0eba96fd5e
commit b0591d9c7e
5 changed files with 68 additions and 20 deletions

View File

@ -26,7 +26,8 @@ from users.schema import AllUsersQuery, UsersQuery, ProfileMutations
class Query(UsersQuery, AllUsersQuery, ModuleRoomsQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, class Query(UsersQuery, AllUsersQuery, ModuleRoomsQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery,
StudentSubmissionQuery, InstrumentQuery, PortfolioQuery, SurveysQuery, AllNewsTeasersQuery, graphene.ObjectType): StudentSubmissionQuery, InstrumentQuery, PortfolioQuery, SurveysQuery, AllNewsTeasersQuery,
graphene.ObjectType):
node = relay.Node.Field() node = relay.Node.Field()
if settings.DEBUG: if settings.DEBUG:
@ -34,8 +35,8 @@ class Query(UsersQuery, AllUsersQuery, ModuleRoomsQuery, RoomsQuery, ObjectivesQ
class Mutation(BookMutations, RoomMutations, AssignmentMutations, ObjectiveMutations, OauthMutations, class Mutation(BookMutations, RoomMutations, AssignmentMutations, ObjectiveMutations, OauthMutations,
PortfolioMutations, ProfileMutations, SurveyMutations, NoteMutations, SpellCheckMutations, PortfolioMutations, ProfileMutations, SurveyMutations, NoteMutations, SpellCheckMutations,
graphene.ObjectType): graphene.ObjectType):
if settings.DEBUG: if settings.DEBUG:
debug = graphene.Field(DjangoDebug, name='_debug') debug = graphene.Field(DjangoDebug, name='_debug')

View File

@ -5,7 +5,7 @@ from graphql_relay import from_global_id
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from api.utils import get_object from api.utils import get_object
from assignments.models import Assignment, SubmissionFeedback from assignments.models import Assignment, StudentSubmission, SubmissionFeedback
from assignments.schema.types import AssignmentNode, StudentSubmissionNode, SubmissionFeedbackNode from assignments.schema.types import AssignmentNode, StudentSubmissionNode, SubmissionFeedbackNode
from .inputs import AssignmentInput, SubmissionFeedbackInput from .inputs import AssignmentInput, SubmissionFeedbackInput
@ -57,18 +57,23 @@ class UpdateSubmissionFeedback(relay.ClientIDMutation):
submission_feedback_data = kwargs.get('submission_feedback') submission_feedback_data = kwargs.get('submission_feedback')
user = info.context.user user = info.context.user
student_submission_id = from_global_id(submission_feedback_data['student_submission'])[1] student_submission_id = from_global_id(submission_feedback_data['student_submission'])[1]
student_submission = StudentSubmission.objects.get(id=student_submission_id)
if not user.is_teacher(): if not user.is_teacher() or not student_submission.student in user.users_in_active_school_class():
raise PermissionDenied('Missing permissions') raise PermissionDenied('Missing permissions')
# todo: replace with middleware # todo: replace with middleware
if user.read_only: if user.read_only:
raise PermissionError('No valid license') raise PermissionError('No valid license')
(submission_feedback, created) = SubmissionFeedback.objects.get_or_create(student_submission_id=student_submission_id) (submission_feedback, created) = SubmissionFeedback.objects.get_or_create(
student_submission_id=student_submission_id,
defaults={
'teacher': user
})
final = submission_feedback_data.get(
final = submission_feedback_data.get('final') if 'final' in submission_feedback_data else submission_feedback.final 'final') if 'final' in submission_feedback_data else submission_feedback.final
submission_feedback.teacher = user submission_feedback.teacher = user
submission_feedback.final = final submission_feedback.final = final

View File

@ -1,6 +1,8 @@
import graphene import graphene
from graphene import relay from graphene import relay
from api.utils import get_object
from assignments.models import StudentSubmission
from assignments.schema.types import AssignmentNode, StudentSubmissionNode from assignments.schema.types import AssignmentNode, StudentSubmissionNode
@ -9,5 +11,14 @@ class AssignmentsQuery(object):
assignments = graphene.List(AssignmentNode) assignments = graphene.List(AssignmentNode)
class StudentSubmissionQuery(object):
student_submission = relay.Node.Field(StudentSubmissionNode) class StudentSubmissionQuery:
student_submission = graphene.Field(StudentSubmissionNode, id=graphene.ID(required=True))
def resolve_student_submission(root, info, *args, **kwargs):
user = info.context.user
id = kwargs.get('id')
submission = get_object(StudentSubmission, id)
if user == submission.student or (user.is_teacher() and submission.student in user.users_in_active_school_class()):
return root
return None

View File

@ -22,19 +22,20 @@ class StudentSubmissionNode(DjangoObjectType):
filter_fields = [] filter_fields = []
interfaces = (relay.Node,) interfaces = (relay.Node,)
def resolve_submission_feedback(self, info, **kwargs): @staticmethod
def resolve_submission_feedback(root: StudentSubmission, info, **kwargs):
user = info.context.user user = info.context.user
if not hasattr(self, 'submission_feedback'): if not hasattr(root, 'submission_feedback'):
return None return None
# teacher path # teacher path
if user.is_teacher(): if user.is_teacher() and root.student in user.users_in_active_school_class():
return self.submission_feedback return root.submission_feedback
# student path # student path
if self.submission_feedback.final: if root.submission_feedback.final:
return self.submission_feedback return root.submission_feedback
return None return None

View File

@ -33,7 +33,7 @@ class SubmissionFeedbackTestCase(SkillboxTestCase):
self.student_submission_id = to_global_id('StudentSubmissionNode', self.student_submission.pk) self.student_submission_id = to_global_id('StudentSubmissionNode', self.student_submission.pk)
school_class = SchoolClassFactory() school_class = SchoolClassFactory()
for user in [self.student1, self.teacher, self.teacher2]: for user in [self.student1, self.teacher]:
SchoolClassMember.objects.create( SchoolClassMember.objects.create(
user=user, user=user,
school_class=school_class school_class=school_class
@ -94,6 +94,24 @@ class SubmissionFeedbackTestCase(SkillboxTestCase):
'id': self.assignment_id 'id': self.assignment_id
}) })
def _fetch_submission_teacher(self, user):
client = self.get_client(user)
query = '''
query StudentSubmission($id: ID!) {
studentSubmission(id: $id) {
id
text
document
submissionFeedback {
text
}
}
}
'''
return client.execute(query, variables={
'id': self.student_submission_id
})
def _fetch_submission_feedback(self, user): def _fetch_submission_feedback(self, user):
client = create_client(user) client = create_client(user)
query = ''' query = '''
@ -125,6 +143,8 @@ class SubmissionFeedbackTestCase(SkillboxTestCase):
def test_student_cannot_create_feedback(self): def test_student_cannot_create_feedback(self):
result = self._create_submission_feedback(self.student1, False, 'Balalal', self.student_submission_id) result = self._create_submission_feedback(self.student1, False, 'Balalal', self.student_submission_id)
self.assertIsNotNone(result.errors) self.assertIsNotNone(result.errors)
self.assertEqual(len(result.errors), 1)
self.assertEqual(result.errors[0].get('message'), 'Missing permissions')
def test_teacher_can_update_feedback(self): def test_teacher_can_update_feedback(self):
assignment = AssignmentFactory( assignment = AssignmentFactory(
@ -146,7 +166,7 @@ class SubmissionFeedbackTestCase(SkillboxTestCase):
self.assertTrue(submission_feedback_response.get('final')) self.assertTrue(submission_feedback_response.get('final'))
self.assertEqual(submission_feedback_response.get('text'), 'Some') self.assertEqual(submission_feedback_response.get('text'), 'Some')
def test_rogue_teacher_cannot_update_feedback(self): def test_external_teacher_cannot_update_feedback(self):
assignment = AssignmentFactory( assignment = AssignmentFactory(
owner=self.teacher owner=self.teacher
) )
@ -183,11 +203,21 @@ class SubmissionFeedbackTestCase(SkillboxTestCase):
self.assertEqual(result.data.get('assignment').get('submissions')[0].get('submissionFeedback') self.assertEqual(result.data.get('assignment').get('submissions')[0].get('submissionFeedback')
.get('text'), submission_feedback.text) .get('text'), submission_feedback.text)
def test_rogue_teacher_cannot_see_feedback(self): def test_external_teacher_cannot_see_assignment_with_feedback(self):
SubmissionFeedbackFactory(teacher=self.teacher, final=False, SubmissionFeedbackFactory(teacher=self.teacher, final=False,
student_submission=self.student_submission) student_submission=self.student_submission)
self.student_submission.final = True self.student_submission.final = True
self.student_submission.save() self.student_submission.save()
result = self._fetch_assignment_teacher(self.teacher2) result = self._fetch_assignment_teacher(self.teacher2)
self.assertIsNone(result.data.get('assignment').get('submissions')[0].get('submissionFeedback')) self.assertEqual(result.data.get('assignment').get('submissions'), [])
def test_external_teacher_cannot_see_feedback(self):
SubmissionFeedbackFactory(teacher=self.teacher, final=False,
student_submission=self.student_submission)
self.student_submission.final = True
self.student_submission.save()
result = self._fetch_submission_teacher(self.teacher2)
self.assertIsNone(result.errors)
self.assertIsNone(result.data.get('studentSubmission'))