Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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())
Example #4
0
    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)
Example #5
0
    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())
Example #6
0
    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)
Example #7
0
    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 -&gt; in the response -> just replace that when looking for
        self.assertContains(response, str(res[0]).replace(">", "&gt;"))
        self.assertContains(response, str(res[1]).replace(">", "&gt;"))

        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)
Example #8
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)