From 634da273478b79600ea8762e11c5f4a483e416f2 Mon Sep 17 00:00:00 2001 From: David Ormsbee Date: Sun, 29 Sep 2013 23:44:38 -0400 Subject: [PATCH] Add AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING flag so that acceptance tests can run through certificate identity verification flow. --- .../courseware/features/certificates.feature | 95 +++++++++---------- lms/djangoapps/verify_student/models.py | 62 ++++++++++-- lms/envs/acceptance.py | 4 + lms/envs/common.py | 5 +- lms/envs/dev.py | 1 + 5 files changed, 107 insertions(+), 60 deletions(-) diff --git a/lms/djangoapps/courseware/features/certificates.feature b/lms/djangoapps/courseware/features/certificates.feature index 18d1c0a19d..1b385b32e5 100644 --- a/lms/djangoapps/courseware/features/certificates.feature +++ b/lms/djangoapps/courseware/features/certificates.feature @@ -9,68 +9,61 @@ Feature: LMS.Verified certificates When I select the audit track Then I should see the course on my dashboard - # There is currently no way to avoid trying to upload - # the photos to S3 for processing - #Scenario: I can submit photos to verify my identity - # Given I am logged in - # When I select the verified track - # And I go to step "1" - # And I capture my "face" photo - # And I approve my "face" photo - # And I go to step "2" - # And I capture my "photo_id" photo - # And I approve my "photo_id" photo - # And I go to step "3" - # And I select a contribution amount - # And I confirm that the details match - # And I go to step "4" - # Then I am at the payment page + Scenario: I can submit photos to verify my identity + Given I am logged in + When I select the verified track + And I go to step "1" + And I capture my "face" photo + And I approve my "face" photo + And I go to step "2" + And I capture my "photo_id" photo + And I approve my "photo_id" photo + And I go to step "3" + And I select a contribution amount + And I confirm that the details match + And I go to step "4" + Then I am at the payment page - # There is currently no way to avoid trying to upload - # the photos to S3 for processing - #Scenario: I can pay for a verified certificate - # Given I have submitted photos to verify my identity - # When I submit valid payment information - # Then I see that my payment was successful + Scenario: I can pay for a verified certificate + Given I have submitted photos to verify my identity + When I submit valid payment information + Then I see that my payment was successful - - # There is currently no way to avoid trying to upload - # the photos to S3 for processing - #Scenario: Verified courses display correctly on dashboard - # Given I have submitted photos to verify my identity - # When I submit valid payment information - # And I navigate to my dashboard - # Then I see the course on my dashboard - # And I see that I am on the verified track + Scenario: Verified courses display correctly on dashboard + Given I have submitted photos to verify my identity + When I submit valid payment information + And I navigate to my dashboard + Then I see the course on my dashboard + And I see that I am on the verified track # Not easily automated - #Scenario: I can re-take photos - # Given I have submitted my "" photo - # When I retake my "" photo - # Then I see the new photo on the confirmation page. - # - # Examples: - # | PhotoType | - # | face | - # | ID | +# Scenario: I can re-take photos +# Given I have submitted my "" photo +# When I retake my "" photo +# Then I see the new photo on the confirmation page. +# +# Examples: +# | PhotoType | +# | face | +# | ID | - # Not yet implemented LMS-983 - #Scenario: I can edit identity information - # Given I have submitted face and ID photos - # When I edit my name - # Then I see the new name on the confirmation page. +# # TODO: automate +# Scenario: I can edit identity information +# Given I have submitted face and ID photos +# When I edit my name +# Then I see the new name on the confirmation page. Scenario: I can return to the verify flow Given I have submitted photos to verify my identity When I leave the flow and return Then I am at the verified page - # Currently broken LMS-1009 - #Scenario: I can pay from the return flow - # Given I have submitted photos to verify my identity - # When I leave the flow and return - # And I press the payment button - # Then I am at the payment page + # TODO: automate +# Scenario: I can pay from the return flow +# Given I have submitted photos to verify my identity +# When I leave the flow and return +# And I press the payment button +# Then I am at the payment page Scenario: I can take a verified certificate course for free Given I am logged in diff --git a/lms/djangoapps/verify_student/models.py b/lms/djangoapps/verify_student/models.py index 80e05d09b1..023c4caca6 100644 --- a/lms/djangoapps/verify_student/models.py +++ b/lms/djangoapps/verify_student/models.py @@ -259,11 +259,6 @@ class PhotoVerification(StatusModel): they uploaded are good. Note that we don't actually do a submission anywhere yet. """ - # if not self.face_image_url: - # raise VerificationException("No face image was uploaded.") - # if not self.photo_id_image_url: - # raise VerificationException("No photo ID image was uploaded.") - # At any point prior to this, they can change their names via their # student dashboard. But at this point, we lock the value into the # attempt. @@ -414,6 +409,21 @@ class SoftwareSecurePhotoVerification(PhotoVerification): @status_before_must_be("created") def upload_face_image(self, img_data): + """ + Upload an image of the user's face to S3. `img_data` should be a raw + bytestream of a PNG image. This method will take the data, encrypt it + using our FACE_IMAGE_AES_KEY, encode it with base64 and save it to S3. + + Yes, encoding it to base64 adds compute and disk usage without much real + benefit, but that's what the other end of this API is expecting to get. + """ + # Skip this whole thing if we're running acceptance tests or if we're + # developing and aren't interested in working on student identity + # verification functionality. If you do want to work on it, you have to + # explicitly enable these in your private settings. + if settings.MITX_FEATURES.get('AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'): + return + aes_key_str = settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["FACE_IMAGE_AES_KEY"] aes_key = aes_key_str.decode("hex") @@ -422,6 +432,23 @@ class SoftwareSecurePhotoVerification(PhotoVerification): @status_before_must_be("created") def upload_photo_id_image(self, img_data): + """ + Upload an the user's photo ID image to S3. `img_data` should be a raw + bytestream of a PNG image. This method will take the data, encrypt it + using a randomly generated AES key, encode it with base64 and save it to + S3. The random key is also encrypted using Software Secure's public RSA + key and stored in our `photo_id_key` field. + + Yes, encoding it to base64 adds compute and disk usage without much real + benefit, but that's what the other end of this API is expecting to get. + """ + # Skip this whole thing if we're running acceptance tests or if we're + # developing and aren't interested in working on student identity + # verification functionality. If you do want to work on it, you have to + # explicitly enable these in your private settings. + if settings.MITX_FEATURES.get('AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'): + return + aes_key = random_aes_key() rsa_key_str = settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["RSA_PUBLIC_KEY"] rsa_encrypted_aes_key = rsa_encrypt(aes_key, rsa_key_str) @@ -436,6 +463,11 @@ class SoftwareSecurePhotoVerification(PhotoVerification): @status_before_must_be("must_retry", "ready", "submitted") def submit(self): + """ + Submit our verification attempt to Software Secure for validation. This + will set our status to "submitted" if the post is successful, and + "must_retry" if the post fails. + """ try: response = self.send_request() if response.ok: @@ -459,7 +491,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): def _generate_key(self, prefix): """ - face/4dd1add9-6719-42f7-bea0-115c008c4fca + Example: face/4dd1add9-6719-42f7-bea0-115c008c4fca """ conn = S3Connection( settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["AWS_ACCESS_KEY"], @@ -517,7 +549,7 @@ class SoftwareSecurePhotoVerification(PhotoVerification): return headers, body def request_message_txt(self): - """ This is the body of the request we send across """ + """This is the body of the request we send across.""" headers, body = self.create_request() header_txt = "\n".join( @@ -528,7 +560,21 @@ class SoftwareSecurePhotoVerification(PhotoVerification): return header_txt + "\n\n" + body_txt def send_request(self): - """ sends the request across to the endpoint """ + """ + Assembles a submission to Software Secure and sends it via HTTPS. + + Returns a request.Response() object with the reply we get from SS. + """ + # If AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING is True, we want to + # skip posting anything to Software Secure. We actually don't even + # create the message because that would require encryption and message + # signing that rely on settings.VERIFY_STUDENT values that aren't set + # in dev. So we just pretend like we successfully posted + if settings.MITX_FEATURES.get('AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'): + fake_response = requests.Response() + fake_response.status_code = 200 + return fake_response + headers, body = self.create_request() response = requests.post( settings.VERIFY_STUDENT["SOFTWARE_SECURE"]["API_URL"], diff --git a/lms/envs/acceptance.py b/lms/envs/acceptance.py index 7d2ad15e6c..8b5801ce23 100644 --- a/lms/envs/acceptance.py +++ b/lms/envs/acceptance.py @@ -81,6 +81,10 @@ MITX_FEATURES['ENABLE_DISCUSSION_SERVICE'] = True # Use the auto_auth workflow for creating users and logging them in MITX_FEATURES['AUTOMATIC_AUTH_FOR_TESTING'] = True +# Don't actually send any requests to Software Secure for student identity +# verification. +MITX_FEATURES['AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'] = True + # Enable fake payment processing page MITX_FEATURES['ENABLE_PAYMENT_FAKE'] = True diff --git a/lms/envs/common.py b/lms/envs/common.py index 96b304294d..0542853101 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -179,6 +179,9 @@ MITX_FEATURES = { # Enable flow for payments for course registration (DIFFERENT from verified student flow) 'ENABLE_PAID_COURSE_REGISTRATION': False, + + # Automatically approve student identity verification attempts + 'AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING': False, } # Used for A/B testing @@ -587,7 +590,7 @@ MIDDLEWARE_CLASSES = ( # catches any uncaught RateLimitExceptions and returns a 403 instead of a 500 'ratelimitbackend.middleware.RateLimitMiddleware', - + # For A/B testing 'waffle.middleware.WaffleMiddleware', ) diff --git a/lms/envs/dev.py b/lms/envs/dev.py index c596208b3f..43b3ef5d15 100644 --- a/lms/envs/dev.py +++ b/lms/envs/dev.py @@ -32,6 +32,7 @@ MITX_FEATURES['ENABLE_HINTER_INSTRUCTOR_VIEW'] = True MITX_FEATURES['ENABLE_INSTRUCTOR_BETA_DASHBOARD'] = True MITX_FEATURES['MULTIPLE_ENROLLMENT_ROLES'] = True MITX_FEATURES['ENABLE_SHOPPING_CART'] = True +MITX_FEATURES['AUTOMATIC_VERIFY_STUDENT_IDENTITY_FOR_TESTING'] = True FEEDBACK_SUBMISSION_EMAIL = "dummy@example.com"