def test_add_status_from_checkpoints(self, status): """Test verification status for reverification checkpoints after submitting software secure photo verification. """ # add initial verification status for checkpoints initial_status = "submitted" VerificationStatus.add_verification_status( checkpoint=self.first_checkpoint, user=self.user, status=initial_status ) VerificationStatus.add_verification_status( checkpoint=self.second_checkpoint, user=self.user, status=initial_status ) # now add verification status for multiple checkpoint points VerificationStatus.add_status_from_checkpoints( checkpoints=[self.first_checkpoint, self.second_checkpoint], user=self.user, status=status ) # test that verification status entries with new status have been added # for both checkpoints result = VerificationStatus.objects.filter(user=self.user, checkpoint=self.first_checkpoint) self.assertEqual(len(result), len(self.first_checkpoint.checkpoint_status.all())) self.assertEqual( list(result.values_list("checkpoint__checkpoint_location", flat=True)), list(self.first_checkpoint.checkpoint_status.values_list("checkpoint__checkpoint_location", flat=True)), ) result = VerificationStatus.objects.filter(user=self.user, checkpoint=self.second_checkpoint) self.assertEqual(len(result), len(self.second_checkpoint.checkpoint_status.all())) self.assertEqual( list(result.values_list("checkpoint__checkpoint_location", flat=True)), list(self.second_checkpoint.checkpoint_status.values_list("checkpoint__checkpoint_location", flat=True)), )
def test_add_status_from_checkpoints(self, status): """adding verfication status for checkpoints list.""" # adding verification status with multiple points VerificationStatus.add_status_from_checkpoints( checkpoints=[self.check_point1, self.check_point2], user=self.user, status=status ) # getting the status from db. result = VerificationStatus.objects.filter(user=self.user) self.assertEqual(len(result), len([self.check_point1.checkpoint_name, self.check_point2.checkpoint_name])) self.assertEqual(result[0].checkpoint.checkpoint_name, self.check_point1.checkpoint_name) self.assertEqual(result[1].checkpoint.checkpoint_name, self.check_point2.checkpoint_name)
def test_add_status_from_checkpoints(self, status): """Test verification status for reverification checkpoints after submitting software secure photo verification. """ # add initial verification status for checkpoints initial_status = "submitted" VerificationStatus.add_verification_status( checkpoint=self.first_checkpoint, user=self.user, status=initial_status) VerificationStatus.add_verification_status( checkpoint=self.second_checkpoint, user=self.user, status=initial_status) # now add verification status for multiple checkpoint points VerificationStatus.add_status_from_checkpoints( checkpoints=[self.first_checkpoint, self.second_checkpoint], user=self.user, status=status) # test that verification status entries with new status have been added # for both checkpoints result = VerificationStatus.objects.filter( user=self.user, checkpoint=self.first_checkpoint) self.assertEqual(len(result), len(self.first_checkpoint.checkpoint_status.all())) self.assertEqual( list( result.values_list('checkpoint__checkpoint_location', flat=True)), list( self.first_checkpoint.checkpoint_status.values_list( 'checkpoint__checkpoint_location', flat=True))) result = VerificationStatus.objects.filter( user=self.user, checkpoint=self.second_checkpoint) self.assertEqual(len(result), len(self.second_checkpoint.checkpoint_status.all())) self.assertEqual( list( result.values_list('checkpoint__checkpoint_location', flat=True)), list( self.second_checkpoint.checkpoint_status.values_list( 'checkpoint__checkpoint_location', flat=True)))
def handle(self, *args, **kwargs): # pylint: disable=unused-argument from verify_student.views import _set_user_requirement_status status_to_set = args[0] receipt_id = args[1] try: attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=receipt_id) except SoftwareSecurePhotoVerification.DoesNotExist: self.stderr.write( 'SoftwareSecurePhotoVerification with id {id} could not be found.\n'.format(id=receipt_id) ) sys.exit(1) if status_to_set == 'approved': self.stdout.write('Approving verification for {id}.\n'.format(id=receipt_id)) attempt.approve() _set_user_requirement_status(attempt, 'reverification', 'satisfied') elif status_to_set == 'denied': self.stdout.write('Denying verification for {id}.\n'.format(id=receipt_id)) if len(args) >= 3: reason_for_denial = args[2] else: reason_for_denial = 'Denied via management command.' attempt.deny(reason_for_denial) _set_user_requirement_status(attempt, 'reverification', 'failed', reason_for_denial) else: self.stdout.write('Cannot set id {id} to unrecognized status {status}'.format( id=receipt_id, status=status_to_set )) sys.exit(1) checkpoints = VerificationCheckpoint.objects.filter(photo_verification=attempt).all() VerificationStatus.add_status_from_checkpoints( checkpoints=checkpoints, user=attempt.user, status=status_to_set )
def test_add_status_from_checkpoints(self, status): """ Adding verification status for checkpoints list after submitting sspv. """ # add initial verification status for checkpoints initial_status = "submitted" VerificationStatus.add_verification_status( checkpoint=self.check_point1, user=self.user, status=initial_status, location_id=self.dummy_reverification_item_id_1) VerificationStatus.add_verification_status( checkpoint=self.check_point2, user=self.user, status=initial_status, location_id=self.dummy_reverification_item_id_2) # now add verification status for multiple checkpoint points VerificationStatus.add_status_from_checkpoints( checkpoints=[self.check_point1, self.check_point2], user=self.user, status=status) # test that verification status entries with new status have been added # for both checkpoints and all entries have related 'location_id'. result = VerificationStatus.objects.filter( user=self.user, checkpoint=self.check_point1) self.assertEqual(len(result), len(self.check_point1.checkpoint_status.all())) self.assertEqual( list(result.values_list('location_id', flat=True)), list(self.check_point1.checkpoint_status.all().values_list( 'location_id', flat=True))) result = VerificationStatus.objects.filter( user=self.user, checkpoint=self.check_point2) self.assertEqual(len(result), len(self.check_point2.checkpoint_status.all())) self.assertEqual( list(result.values_list('location_id', flat=True)), list(self.check_point2.checkpoint_status.all().values_list( 'location_id', flat=True)))
def test_add_status_from_checkpoints(self, status): """ Adding verification status for checkpoints list after submitting sspv. """ # add initial verification status for checkpoints initial_status = "submitted" VerificationStatus.add_verification_status( checkpoint=self.check_point1, user=self.user, status=initial_status, location_id=self.dummy_reverification_item_id_1 ) VerificationStatus.add_verification_status( checkpoint=self.check_point2, user=self.user, status=initial_status, location_id=self.dummy_reverification_item_id_2 ) # now add verification status for multiple checkpoint points VerificationStatus.add_status_from_checkpoints( checkpoints=[self.check_point1, self.check_point2], user=self.user, status=status ) # test that verification status entries with new status have been added # for both checkpoints and all entries have related 'location_id'. result = VerificationStatus.objects.filter(user=self.user, checkpoint=self.check_point1) self.assertEqual(len(result), len(self.check_point1.checkpoint_status.all())) self.assertEqual( list(result.values_list('location_id', flat=True)), list(self.check_point1.checkpoint_status.all().values_list('location_id', flat=True)) ) result = VerificationStatus.objects.filter(user=self.user, checkpoint=self.check_point2) self.assertEqual(len(result), len(self.check_point2.checkpoint_status.all())) self.assertEqual( list(result.values_list('location_id', flat=True)), list(self.check_point2.checkpoint_status.all().values_list('location_id', flat=True)) )
def results_callback(request): """ Software Secure will call this callback to tell us whether a user is verified to be who they said they are. """ body = request.body try: body_dict = json.loads(body) except ValueError: log.exception("Invalid JSON received from Software Secure:\n\n{}\n".format(body)) return HttpResponseBadRequest("Invalid JSON. Received:\n\n{}".format(body)) if not isinstance(body_dict, dict): log.error("Reply from Software Secure is not a dict:\n\n{}\n".format(body)) return HttpResponseBadRequest("JSON should be dict. Received:\n\n{}".format(body)) headers = { "Authorization": request.META.get("HTTP_AUTHORIZATION", ""), "Date": request.META.get("HTTP_DATE", "") } sig_valid = ssencrypt.has_valid_signature( "POST", headers, body_dict, settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_ACCESS_KEY"], settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_SECRET_KEY"] ) _response, access_key_and_sig = headers["Authorization"].split(" ") access_key = access_key_and_sig.split(":")[0] # This is what we should be doing... #if not sig_valid: # return HttpResponseBadRequest("Signature is invalid") # This is what we're doing until we can figure out why we disagree on sigs if access_key != settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_ACCESS_KEY"]: return HttpResponseBadRequest("Access key invalid") receipt_id = body_dict.get("EdX-ID") result = body_dict.get("Result") reason = body_dict.get("Reason", "") error_code = body_dict.get("MessageType", "") try: attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=receipt_id) except SoftwareSecurePhotoVerification.DoesNotExist: log.error("Software Secure posted back for receipt_id {}, but not found".format(receipt_id)) return HttpResponseBadRequest("edX ID {} not found".format(receipt_id)) checkpoints = VerificationCheckpoint.objects.filter(photo_verification=attempt).all() if result == "PASS": log.debug("Approving verification for {}".format(receipt_id)) attempt.approve() status = "approved" elif result == "FAIL": log.debug("Denying verification for {}".format(receipt_id)) attempt.deny(json.dumps(reason), error_code=error_code) status = "denied" elif result == "SYSTEM FAIL": log.debug("System failure for {} -- resetting to must_retry".format(receipt_id)) attempt.system_error(json.dumps(reason), error_code=error_code) status = "error" log.error("Software Secure callback attempt for %s failed: %s", receipt_id, reason) else: log.error("Software Secure returned unknown result {}".format(result)) return HttpResponseBadRequest( "Result {} not understood. Known results: PASS, FAIL, SYSTEM FAIL".format(result) ) # If this is a reverification, log an event if attempt.window: course_id = attempt.window.course_id course_enrollment = CourseEnrollment.get_or_create_enrollment(attempt.user, course_id) course_enrollment.emit_event(EVENT_NAME_USER_REVERIFICATION_REVIEWED_BY_SOFTWARESECURE) VerificationStatus.add_status_from_checkpoints(checkpoints=checkpoints, user=attempt.user, status=status) return HttpResponse("OK!")
def results_callback(request): """ Software Secure will call this callback to tell us whether a user is verified to be who they said they are. """ body = request.body try: body_dict = json.loads(body) except ValueError: log.exception("Invalid JSON received from Software Secure:\n\n{}\n".format(body)) return HttpResponseBadRequest("Invalid JSON. Received:\n\n{}".format(body)) if not isinstance(body_dict, dict): log.error("Reply from Software Secure is not a dict:\n\n{}\n".format(body)) return HttpResponseBadRequest("JSON should be dict. Received:\n\n{}".format(body)) headers = { "Authorization": request.META.get("HTTP_AUTHORIZATION", ""), "Date": request.META.get("HTTP_DATE", "") } has_valid_signature( "POST", headers, body_dict, settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_ACCESS_KEY"], settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_SECRET_KEY"] ) _response, access_key_and_sig = headers["Authorization"].split(" ") access_key = access_key_and_sig.split(":")[0] # This is what we should be doing... #if not sig_valid: # return HttpResponseBadRequest("Signature is invalid") # This is what we're doing until we can figure out why we disagree on sigs if access_key != settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_ACCESS_KEY"]: return HttpResponseBadRequest("Access key invalid") receipt_id = body_dict.get("EdX-ID") result = body_dict.get("Result") reason = body_dict.get("Reason", "") error_code = body_dict.get("MessageType", "") try: attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=receipt_id) except SoftwareSecurePhotoVerification.DoesNotExist: log.error("Software Secure posted back for receipt_id %s, but not found", receipt_id) return HttpResponseBadRequest("edX ID {} not found".format(receipt_id)) if result == "PASS": log.debug("Approving verification for %s", receipt_id) attempt.approve() status = "approved" _set_user_requirement_status(attempt, 'reverification', 'satisfied') elif result == "FAIL": log.debug("Denying verification for %s", receipt_id) attempt.deny(json.dumps(reason), error_code=error_code) status = "denied" _set_user_requirement_status( attempt, 'reverification', 'failed', json.dumps(reason) ) elif result == "SYSTEM FAIL": log.debug("System failure for %s -- resetting to must_retry", receipt_id) attempt.system_error(json.dumps(reason), error_code=error_code) status = "error" log.error("Software Secure callback attempt for %s failed: %s", receipt_id, reason) else: log.error("Software Secure returned unknown result %s", result) return HttpResponseBadRequest( "Result {} not understood. Known results: PASS, FAIL, SYSTEM FAIL".format(result) ) incourse_reverify_enabled = InCourseReverificationConfiguration.current().enabled if incourse_reverify_enabled: checkpoints = VerificationCheckpoint.objects.filter(photo_verification=attempt).all() VerificationStatus.add_status_from_checkpoints(checkpoints=checkpoints, user=attempt.user, status=status) # If this is re-verification then send the update email if checkpoints: user_id = attempt.user.id course_key = checkpoints[0].course_id related_assessment_location = checkpoints[0].checkpoint_location subject, message = _compose_message_reverification_email( course_key, user_id, related_assessment_location, status, request ) _send_email(user_id, subject, message) return HttpResponse("OK!")