def test_student_cannot_download_answer_file_not_owned(self): s_exercise = SubmissionExercise.objects.get(name="T1 TEXT") r_exercise = ReviewExercise.objects.get(name="T1 TEXT REVIEW") sub = OriginalSubmission(course=self.course, text="juups", submitter_user=self.s1, exercise=s_exercise) sub.save() r_exercise.question_order = ['3'] r_exercise.save() rev_sub = ReviewSubmission(course=self.course, reviewed_submission=sub, submitter_user=self.s2, exercise=r_exercise) rev_sub.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) answer_with_file = Answer( question=Question.objects.get(pk=3), submission=rev_sub, uploaded_file=temp_file, ) answer_with_file.save() request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{answer_with_file.pk}/?type=answer' ) request.user = self.s3 self.kwargs['pk'] = answer_with_file.pk self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs) g = StudentGroup(name="g1", course=self.course, student_usernames=[self.s1.email, self.s3.email]) g.save() sub.submitter_group = g sub.save() for user in self.students + [self.t1]: request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{answer_with_file.pk}/?type=answer' ) request.user = user self.kwargs['pk'] = answer_with_file.pk if user == self.s4: self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs) else: response = DownloadSubmissionView.as_view()(request, **self.kwargs) self.assertEqual(response.status_code, 200)
def post(self, *args, **kwargs): """ TODO: error checking """ self.object = self.get_object() ctx = self.get_context_data() exercise = self.object as_student_form = ChooseStudentForm(self.request.POST, exercise=exercise) answers_valid, answer_forms = ReviewExerciseDetailView._validate_forms(self) if answers_valid and as_student_form.is_valid(): rsub = ReviewSubmission(course=ctx['course'], exercise=self.object, reviewed_submission=as_student_form.cleaned_data['reviewed_submission'], submitter_user=as_student_form.cleaned_data['submitter_user']) if exercise.use_groups: rsub.submitter_group = as_student_form.cleaned_data['submitter_group'] rsub.save() ReviewExerciseDetailView._save_modelform_answers(self, answer_forms, rsub) messages.success(self.request, 'Submission successful! You may see it below.') return redirect(rsub) ctx['forms'] = answer_forms ctx['choose_student_form'] = as_student_form return self.render_to_response(ctx)
def _post_random(self, ctx): rlock_list = self.object.reviewlocks_for(self.request.user) rlock = rlock_list.last() reviewed_submission = None lock_was_expired = False if rlock: reviewed_submission = rlock.original_submission elif not rlock and self.object.reviewlock_expiry_hours != 0: # if the expiry time has been set and a user keeps the exercise page # visible for a very long time without refreshing, it is possible that # whenever the form is actually submitted the original reviewlock has # already expired and deleted. in that situation, from the system's POV, # the user is making a bogus form submission since no lock is held. # if this "race condition" happens, grab the originally reviewed submission's # PK from the submitted form. # # this is not beautiful, but serves the purpose. other implementation # possibilities could be to just show an error page but that would waste # the peer-reviewing efforts by the student. some sort of verification could # also be implemented client-side in JavaScript to show the user a warning or # something. this, however, is the easiest solution. reviewed_submission = self.object.reviewable_exercise.submissions.get(pk=self.request.POST.get('choice')) lock_was_expired = True new_review_submission = ReviewSubmission(course=ctx['course'], submitter_user=self.request.user, exercise=self.object, reviewed_submission=reviewed_submission) if self.object.use_groups: new_review_submission.submitter_group = ctx['my_group'] if lock_was_expired: new_review_submission.save() else: new_review_submission.save_and_destroy_lock() self._save_modelform_answers(self.answer_forms, new_review_submission) if self.request.LTI_MODE: return _construct_aplus_response(exercise=self.object, user=self.request.user) return HttpResponseRedirect(new_review_submission.get_absolute_url())
def test_student_cannot_download_hide_from_receiver_answer_file(self): s_exercise = SubmissionExercise.objects.get(name="T1 TEXT") r_exercise = ReviewExercise.objects.get(name="T1 TEXT REVIEW") sub = OriginalSubmission(course=self.course, text="juups", submitter_user=self.s1, exercise=s_exercise) sub.save() r_exercise.question_order = ['3'] r_exercise.save() rev_sub = ReviewSubmission(course=self.course, reviewed_submission=sub, submitter_user=self.s2, exercise=r_exercise) rev_sub.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) question = Question.objects.get(pk=3) answer_with_file = Answer( question=question, submission=rev_sub, uploaded_file=temp_file, ) answer_with_file.save() request = self.factory.get( f'/courses/prog1/F2018/submissions/download/{answer_with_file.pk}/?type=answer' ) request.user = self.s1 self.kwargs['pk'] = answer_with_file.pk self.assertEqual( DownloadSubmissionView.as_view()(request, **self.kwargs).status_code, 200) question.hide_from_receiver = True question.save() self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs)
def _post_choose(self, ctx): cf = ChooseForm(self.request.POST, exercise=self.object, user=self.request.user) if not cf.is_valid(): # this prevents fiddling with html form fields in dev tools raise PermissionDenied('You cannot do that. If you believe this is an error, contact admin.') # if ChooseForm is valid, construct a new ReviewSubmission reviewed_submission = OriginalSubmission.objects.get(pk=self.request.POST.get('choice')) new_review_submission = ReviewSubmission(course=ctx['course'], submitter_user=self.request.user, exercise=self.object, reviewed_submission=reviewed_submission) if self.object.use_groups: new_review_submission.submitter_group = ctx['my_group'] new_review_submission.save() self._save_modelform_answers(self.answer_forms, new_review_submission) if self.request.LTI_MODE: return _construct_aplus_response(exercise=self.object, user=self.request.user) return HttpResponseRedirect(new_review_submission.get_absolute_url())
def test_reviewexercise_type_group(self): se = SubmissionExercise.objects.get(name="T1 TEXT") re = ReviewExercise.objects.get(name="T1 TEXT REVIEW") se.type = SubmissionExercise.GROUP_NO_SUBMISSION se.save() re.type = ReviewExercise.GROUP re.use_groups = True re.save() temp_email = "*****@*****.**" g = StudentGroup( course=se.course, name="g1", student_usernames=[self.s1.email, self.s2.email, temp_email]) g.save() user_count_before = User.objects.count() self.assertEqual(0, se.submissions.count()) request = self.factory.get(re.get_absolute_url()) request.user = self.s1 self.kwargs['pk'] = re.pk # should create temp user for [email protected] # should have three orig subs because three group members res = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertEqual(user_count_before + 1, User.objects.count()) self.assertEqual(3, se.submissions.count()) # when students review each other, inlined options are not shown, only ChooseForm dropdown self.assertEqual(len(res.context_data['chooseform_options_list']), 0) temp_user = User.objects.get(email=temp_email) self.assertEqual(temp_user.temporary, True) self.assertEqual(temp_user.lti, False) request.user = self.s2 ReviewExerciseDetailView.as_view()(request, **self.kwargs) # page load as another user of the group shouldn't have any effect self.assertEqual(user_count_before + 1, User.objects.count()) self.assertEqual(3, se.submissions.count()) orig_subs_for_temp_user = OriginalSubmission.objects.filter( submitter_user=temp_user) self.assertEqual(1, orig_subs_for_temp_user.count()) resub = ReviewSubmission( course=re.course, exercise=re, submitter_user=self.s1, reviewed_submission=orig_subs_for_temp_user.first(), ) resub.save() # create actual user. this will be used to simulate sihbboleth login in a future time # when the system allready has a submission temporarily made for this user. actual_user = User.objects.create(username="******", email=temp_email) actual_user.set_password("actual_password") actual_user.save() self.assertEqual(2, User.objects.filter(email=temp_email).count()) orig_submitter = OriginalSubmission.objects.get( submitter_user__email=temp_email).submitter_user.username # logging in a user would normally trigger users/receivers.py which will: # - find all submissions done with a temp user using same email # - transfer all submissions to this actual user # - delete temp user # here we'll just call the signal handler directly fake_request = HttpRequest() fake_request.LTI_MODE = False fake_request.user = actual_user change_original_submission_submitters(None, request=fake_request, user=actual_user) actual_submitter = OriginalSubmission.objects.get( submitter_user__email=temp_email).submitter_user.username self.assertNotEqual(orig_submitter, actual_submitter) self.assertEqual(1, User.objects.filter(email=temp_email).count()) actual_user = User.objects.get(email=temp_email) self.assertEqual(actual_user.temporary, False) request.user = actual_user response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) self.assertEqual(user_count_before + 1, User.objects.all().count()) self.assertEqual(3, se.submissions.count()) # nothing, student1, student2 self.assertEqual( str(response.context_data['chooseForm']).count("<option"), 3) re.can_review_own_submission = True re.save() response = ReviewExerciseDetailView.as_view()(request, **self.kwargs) # nothing, student1, student2, actual_user self.assertEqual( str(response.context_data['chooseForm']).count("<option"), 4) # actual_user can see one review by another student (student1) request = self.factory.get(re.get_list_url()) request.user = actual_user self.kwargs['pk'] = re.pk response = ReviewSubmissionListView.as_view()(request, **self.kwargs) self.assertEqual( response.context_data['reviewsubmission_list'].count(), 1)
def test_original_submission_delete_confirmation_page_shows_cascades(self): os = OriginalSubmission( course=self.course, exercise=SubmissionExercise.objects.get(name="T1 TEXT"), submitter_user=self.s1, text="jada jada jada jada", ) os.save() request = self.factory.get(os.get_delete_url()) self.kwargs['pk'] = os.exercise.pk self.kwargs['sub_pk'] = os.pk request.user = self.s1 # students cannot access self.assertRaises(PermissionDenied, OriginalSubmissionDeleteView.as_view(), request, **self.kwargs) request.user = self.t1 response = OriginalSubmissionDeleteView.as_view()(request, **self.kwargs) self.assertContains(response, "No peer-reviews exist yet") res = [] for s in [self.s2, self.s3]: re = ReviewSubmission( course=self.course, exercise=ReviewExercise.objects.get(name="T1 TEXT REVIEW"), reviewed_submission=os, submitter_user=s, ) re.save() res.append(re) response = OriginalSubmissionDeleteView.as_view()(request, **self.kwargs) self.assertNotContains(response, "No peer-reviews exist yet") # -> is turned into -> in the response -> just replace that when looking for self.assertContains(response, str(res[0]).replace(">", ">")) self.assertContains(response, str(res[1]).replace(">", ">")) self.assertEqual(OriginalSubmission.objects.count(), 1) self.assertEqual(ReviewSubmission.objects.count(), 2) request = self.factory.get(res[0].get_delete_url()) self.kwargs['pk'] = res[0].exercise.pk self.kwargs['sub_pk'] = res[0].pk request.user = self.s1 # students cannot access self.assertRaises(PermissionDenied, ReviewSubmissionDeleteView.as_view(), request, **self.kwargs) # delete page doesn't list any useful information, no need to check os.delete() self.assertEqual(OriginalSubmission.objects.count(), 0) self.assertEqual(ReviewSubmission.objects.count(), 0)
def test_download_tokens_allow_downloading(self): exercise = SubmissionExercise.objects.get(pk=1) exercise.type = 'FILE_UPLOAD' exercise.accepted_filetypes = '.txt' exercise.save() temp_file = SimpleUploadedFile(name='lorem_ipsum.txt', content=bytearray('jada jada', 'utf-8')) sub = OriginalSubmission(course=self.course, file=temp_file, submitter_user=self.s1, exercise=exercise) sub.save() rev = ReviewSubmission(course=self.course, submitter_user=self.s1, exercise=exercise.review_exercise, reviewed_submission=sub) rev.save() answer_with_file = Answer( question=Question.objects.get(pk=3), submission=rev, uploaded_file=temp_file, ) answer_with_file.save() view_request = self.factory.get(exercise.get_absolute_url() + "?some=queryparams") osub_token = sub.get_download_token_for(self.s1, view_request) view_request = self.factory.get( exercise.review_exercise.get_absolute_url() + "?some=queryparams") rsub_token = rev.get_download_token_for(self.s1, view_request) cases = [ (AnonymousUser, 403, sub, ""), (AnonymousUser, 200, sub, f"?dl_token={osub_token}"), (AnonymousUser, 403, sub, f"?dl_token={osub_token}_THIS_SHOULD_CAUSE_PROBLEMS"), (AnonymousUser, 403, answer_with_file, ""), (AnonymousUser, 200, answer_with_file, f"&dl_token={rsub_token}"), (AnonymousUser, 403, answer_with_file, f"&dl_token={rsub_token}_THIS_SHOULD_CAUSE_PROBLEMS"), ] for user, code, sub, query in cases: url = f"{sub.get_file_download_url()}{query}" request = self.factory.get(url) request.user = user self.kwargs['pk'] = sub.pk if code == 403: self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs) else: response = DownloadSubmissionView.as_view()(request, **self.kwargs) self.assertEqual(response.status_code, code) DownloadToken.objects.all().delete() for user, _, sub, query in cases: url = f"{sub.get_file_download_url()}{query}" request = self.factory.get(url) request.user = user self.kwargs['pk'] = sub.pk self.assertRaises(PermissionDenied, DownloadSubmissionView.as_view(), request, **self.kwargs)