diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index fbc9cdc015..c761835db8 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -593,18 +593,22 @@ class SoftwareSecurePhotoVerification(PhotoVerification): copy_id_photo_from = models.ForeignKey("self", null=True, blank=True) @classmethod - def get_initial_verification(cls, user): + def get_initial_verification(cls, user, earliest_allowed_date=None): """Get initial verification for a user with the 'photo_id_key'. Arguments: user(User): user object + earliest_allowed_date(datetime): override expiration date for initial verification Return: - SoftwareSecurePhotoVerification (object) + SoftwareSecurePhotoVerification (object) or None """ init_verification = cls.objects.filter( user=user, - status__in=["submitted", "approved"] + status__in=["submitted", "approved"], + created_at__gte=( + earliest_allowed_date or cls._earliest_allowed_date() + ) ).exclude(photo_id_key='') return init_verification.latest('created_at') if init_verification.exists() else None diff --git a/lms/djangoapps/verify_student/tests/test_models.py b/lms/djangoapps/verify_student/tests/test_models.py index 07fa37548f..349cf7d144 100644 --- a/lms/djangoapps/verify_student/tests/test_models.py +++ b/lms/djangoapps/verify_student/tests/test_models.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- from datetime import timedelta, datetime -import ddt import json -import mock -import requests.exceptions -import pytz +import ddt from django.conf import settings from django.db import IntegrityError from django.test import TestCase +from freezegun import freeze_time +import mock from mock import patch from nose.tools import assert_is_none, assert_equals, assert_raises, assert_true, assert_false # pylint: disable=no-name-in-module +import pytz +import requests.exceptions from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase @@ -42,8 +43,9 @@ iwIDAQAB "API_URL": "http://localhost/verify_student/fake_endpoint", "AWS_ACCESS_KEY": "FAKEACCESSKEY", "AWS_SECRET_KEY": "FAKESECRETKEY", - "S3_BUCKET": "fake-bucket" - } + "S3_BUCKET": "fake-bucket", + }, + "DAYS_GOOD_FOR": 10, } @@ -524,6 +526,21 @@ class TestPhotoVerification(ModuleStoreTestCase): self.assertIsNotNone(second_result) self.assertEqual(second_result, first_result) + # Test method 'get_initial_verification' returns None after expiration + expired_future = datetime.utcnow() + timedelta(days=(FAKE_SETTINGS['DAYS_GOOD_FOR'] + 1)) + with freeze_time(expired_future): + third_result = SoftwareSecurePhotoVerification.get_initial_verification(user) + self.assertIsNone(third_result) + + # Test method 'get_initial_verification' returns correct attempt after system expiration, + # but within earliest allowed override. + expired_future = datetime.utcnow() + timedelta(days=(FAKE_SETTINGS['DAYS_GOOD_FOR'] + 1)) + earliest_allowed = datetime.utcnow() - timedelta(days=1) + with freeze_time(expired_future): + fourth_result = SoftwareSecurePhotoVerification.get_initial_verification(user, earliest_allowed) + self.assertIsNotNone(fourth_result) + self.assertEqual(fourth_result, first_result) + @ddt.ddt class VerificationCheckpointTest(ModuleStoreTestCase): diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 4852334528..2a876035ed 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -1427,7 +1427,8 @@ class TestSubmitPhotosForVerification(TestCase): "AWS_ACCESS_KEY": "c987c7efe35c403caa821f7328febfa1", "AWS_SECRET_KEY": "fc595fc657c04437bb23495d8fe64881", "S3_BUCKET": "test.example.com", - } + }, + "DAYS_GOOD_FOR": 10, }) @httpretty.activate @moto.mock_s3 @@ -1470,6 +1471,16 @@ class TestSubmitPhotosForVerification(TestCase): self.assertNotEqual(initial_photo_response.content, reverification_photo_response.content) + # Submit a new face photo and photo id for verification + self._submit_photos( + face_image=self.IMAGE_DATA + "9999", + photo_id_image=self.IMAGE_DATA + "1111", + ) + two_photo_reverification_data = self._get_post_data() + + # Verify that the initial attempt sent a new ID photo for the reverification attempt + self.assertNotEqual(initial_data["PhotoIDKey"], two_photo_reverification_data["PhotoIDKey"]) + @ddt.data('face_image', 'photo_id_image') def test_invalid_image_data(self, invalid_param): params = { diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index 431ba89b97..81aa0c2487 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -872,6 +872,11 @@ class SubmitPhotosView(View): face_image, photo_id_image, response = self._decode_image_data( params["face_image"], params.get("photo_id_image") ) + + # If we have a photo_id we do not want use the initial verification image. + if photo_id_image is not None: + initial_verification = None + if response is not None: return response