From 9d54ceea0b997a7ee14152654e60da8fc18bda2f Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Wed, 18 Sep 2013 17:09:14 -0400 Subject: [PATCH] Add unicode handling to the signing and verifying logic --- .../shoppingcart/processors/CyberSource.py | 13 +++++----- .../processors/tests/test_CyberSource.py | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/lms/djangoapps/shoppingcart/processors/CyberSource.py b/lms/djangoapps/shoppingcart/processors/CyberSource.py index 6162b325e3..5d43482bd4 100644 --- a/lms/djangoapps/shoppingcart/processors/CyberSource.py +++ b/lms/djangoapps/shoppingcart/processors/CyberSource.py @@ -54,7 +54,7 @@ def processor_hash(value): Performs the base64(HMAC_SHA1(key, value)) used by CyberSource Hosted Order Page """ shared_secret = settings.CC_PROCESSOR['CyberSource'].get('SHARED_SECRET', '') - hash_obj = hmac.new(shared_secret, value, sha1) + hash_obj = hmac.new(shared_secret.encode('utf-8'), value.encode('utf-8'), sha1) return binascii.b2a_base64(hash_obj.digest())[:-1] # last character is a '\n', which we don't want @@ -71,10 +71,10 @@ def sign(params, signed_fields_key='orderPage_signedFields', full_sig_key='order params['orderPage_timestamp'] = int(time.time() * 1000) params['orderPage_version'] = order_page_version params['orderPage_serialNumber'] = serial_number - fields = ",".join(params.keys()) - values = ",".join(["{0}={1}".format(i, params[i]) for i in params.keys()]) + fields = u",".join(params.keys()) + values = u",".join([u"{0}={1}".format(i, params[i]) for i in params.keys()]) fields_sig = processor_hash(fields) - values += ",signedFieldsPublicSignature=" + fields_sig + values += u",signedFieldsPublicSignature=" + fields_sig params[full_sig_key] = processor_hash(values) params[signed_fields_key] = fields @@ -90,13 +90,14 @@ def verify_signatures(params, signed_fields_key='signedFields', full_sig_key='si raises CCProcessorSignatureException if not verified """ signed_fields = params.get(signed_fields_key, '').split(',') - data = ",".join(["{0}={1}".format(k, params.get(k, '')) for k in signed_fields]) + data = u",".join([u"{0}={1}".format(k, params.get(k, '')) for k in signed_fields]) signed_fields_sig = processor_hash(params.get(signed_fields_key, '')) - data += ",signedFieldsPublicSignature=" + signed_fields_sig + data += u",signedFieldsPublicSignature=" + signed_fields_sig returned_sig = params.get(full_sig_key, '') if processor_hash(data) != returned_sig: raise CCProcessorSignatureException() + def render_purchase_form_html(cart): """ Renders the HTML of the hidden POST form that must be used to initiate a purchase with CyberSource diff --git a/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py b/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py index c88d0ca5e0..6f708f3bc3 100644 --- a/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py +++ b/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource.py @@ -58,6 +58,28 @@ class CyberSourceTests(TestCase): # testing for the absence of that exception. the trivial assert below does that self.assertEqual(1, 1) + def test_sign_then_verify_unicode(self): + """ + Similar to the test above, which loops back to the original. + Testing to make sure we can handle unicode parameters + """ + params = { + 'card_accountNumber': '1234', + 'card_cardType': '001', + 'billTo_firstName': u'\u2699', + 'billTo_lastName': u"\u2603", + 'orderNumber': '1', + 'orderCurrency': 'usd', + 'decision': 'ACCEPT', + 'ccAuthReply_amount': '0.00' + } + + verify_signatures(sign(params), signed_fields_key='orderPage_signedFields', + full_sig_key='orderPage_signaturePublic') + # if the above verify_signature fails it will throw an exception, so basically we're just + # testing for the absence of that exception. the trivial assert below does that + self.assertEqual(1, 1) + def test_verify_exception(self): """ Tests that failure to verify raises the proper CCProcessorSignatureException @@ -162,6 +184,7 @@ class CyberSourceTests(TestCase): 'card_accountNumber': '1234', 'card_cardType': '001', 'billTo_firstName': student1.first_name, + 'billTo_lastName': u"\u2603", 'orderNumber': str(order1.id), 'orderCurrency': 'usd', 'decision': 'ACCEPT', @@ -194,6 +217,7 @@ class CyberSourceTests(TestCase): # finally, tests an accepted order self.assertTrue(payment_accepted(params)['accepted']) + @patch('shoppingcart.processors.CyberSource.render_to_string', autospec=True) def test_render_purchase_form_html(self, render): """