def test_signed_payload(mocker, contract_number): """ A valid payload should be signed appropriately """ order = B2BOrderFactory.create(contract_number=contract_number) transaction_uuid = "hex" now = now_in_utc() now_mock = mocker.patch("b2b_ecommerce.api.now_in_utc", autospec=True, return_value=now) product_version = order.product_version product = product_version.product mocker.patch( "b2b_ecommerce.api.uuid.uuid4", autospec=True, return_value=mocker.MagicMock(hex=transaction_uuid), ) receipt_url = "https://example.com/base_url/receipt/" cancel_url = "https://example.com/base_url/cancel/" payload = generate_b2b_cybersource_sa_payload(order=order, receipt_url=receipt_url, cancel_url=cancel_url) signature = payload.pop("signature") assert generate_cybersource_sa_signature(payload) == signature signed_field_names = payload["signed_field_names"].split(",") assert signed_field_names == sorted(payload.keys()) total_price = order.total_price assert payload == { "access_key": CYBERSOURCE_ACCESS_KEY, "amount": str(total_price), "currency": "USD", "item_0_code": "enrollment_code", "item_0_name": f"Enrollment codes for {product_version.description}"[:254], "item_0_quantity": order.num_seats, "item_0_sku": f"enrollment_code-{str(product.content_type)}-{product.content_object.id}", "item_0_tax_amount": "0", "item_0_unit_price": str(total_price), "line_item_count": 1, "locale": "en-us", "reference_number": order.reference_number, "override_custom_receipt_page": receipt_url, "override_custom_cancel_page": cancel_url, "profile_id": CYBERSOURCE_PROFILE_ID, "signed_date_time": now.strftime(ISO_8601_FORMAT), "signed_field_names": ",".join(signed_field_names), "transaction_type": "sale", "transaction_uuid": transaction_uuid, "unsigned_field_names": "", "merchant_defined_data1": order.contract_number or "", } now_mock.assert_called_once_with()
def gen_fake_receipt_data(order=None): """ Helper function to generate a fake signed piece of data """ data = {} for _ in range(10): data[FAKE.text()] = FAKE.text() keys = sorted(data.keys()) data["signed_field_names"] = ",".join(keys) data["unsigned_field_names"] = "" data["req_reference_number"] = order.reference_number if order else "" data["signature"] = generate_cybersource_sa_signature(data) return data
def gen_fake_receipt_data(order=None): """ Helper function to generate a fake signed piece of data """ data = {} for _ in range(10): data[FAKE.text()] = FAKE.text() keys = sorted(data.keys()) data['signed_field_names'] = ",".join(keys) data['unsigned_field_names'] = '' data['req_reference_number'] = make_reference_id(order) if order else '' data['signature'] = generate_cybersource_sa_signature(data) return data
def test_has_signature(settings, mocker): """ If the payload has a valid signature, it should pass the permissions test """ settings.CYBERSOURCE_SECURITY_KEY = "fake" payload = {"a": "b", "c": "d", "e": "f"} keys = sorted(payload.keys()) payload["signed_field_names"] = ",".join(keys) payload["signature"] = generate_cybersource_sa_signature(payload) request = mocker.MagicMock(data=payload) assert IsSignedByCyberSource().has_permission(request, mocker.MagicMock()) is True
def test_signed_payload(self): """ A valid payload should be signed appropriately """ course_run, user = create_purchasable_course_run() order = create_unfulfilled_order(course_run.edx_course_key, user) username = '******' transaction_uuid = 'hex' fake_user_ip = "194.100.0.1" now = now_in_utc() with patch('ecommerce.api.get_social_username', autospec=True, return_value=username): with patch('ecommerce.api.now_in_utc', autospec=True, return_value=now) as now_mock: with patch('ecommerce.api.uuid.uuid4', autospec=True, return_value=MagicMock(hex=transaction_uuid)): payload = generate_cybersource_sa_payload(order, 'dashboard_url', fake_user_ip) signature = payload.pop('signature') assert generate_cybersource_sa_signature(payload) == signature signed_field_names = payload['signed_field_names'].split(',') assert signed_field_names == sorted(payload.keys()) quoted_course_key = quote_plus(course_run.edx_course_key) assert payload == { 'access_key': CYBERSOURCE_ACCESS_KEY, 'amount': str(order.total_price_paid), 'consumer_id': username, 'currency': 'USD', 'item_0_code': 'course', 'item_0_name': '{}'.format(course_run.title), 'item_0_quantity': 1, 'item_0_sku': '{}'.format(course_run.edx_course_key), 'item_0_tax_amount': '0', 'item_0_unit_price': str(order.total_price_paid), 'line_item_count': 1, 'locale': 'en-us', 'override_custom_cancel_page': 'dashboard_url?status=cancel&course_key={}'.format(quoted_course_key), 'override_custom_receipt_page': "dashboard_url?status=receipt&course_key={}".format(quoted_course_key), 'reference_number': make_reference_id(order), 'profile_id': CYBERSOURCE_PROFILE_ID, 'signed_date_time': now.strftime(ISO_8601_FORMAT), 'signed_field_names': ','.join(signed_field_names), 'transaction_type': 'sale', 'transaction_uuid': transaction_uuid, 'unsigned_field_names': '', 'merchant_defined_data1': 'course', 'merchant_defined_data2': '{}'.format(course_run.title), 'merchant_defined_data3': '{}'.format(course_run.edx_course_key), "customer_ip_address": fake_user_ip if fake_user_ip else None, } assert now_mock.called
def test_has_signature(self): """ If the payload has a valid signature, it should pass the permissions test """ payload = { 'a': 'b', 'c': 'd', 'e': 'f', } keys = sorted(payload.keys()) payload['signed_field_names'] = ','.join(keys) payload['signature'] = generate_cybersource_sa_signature(payload) request = MagicMock(data=payload) assert IsSignedByCyberSource().has_permission(request, MagicMock()) is True
def has_permission(self, request, view): """ Returns true if request params are signed by CyberSource """ signature = generate_cybersource_sa_signature(request.data) if request.data['signature'] == signature: return True else: log.error( "Cybersource signature failed: we expected %s but we got %s. Payload: %s", signature, request.data['signature'], request.data, ) return False
def has_permission(self, request, view): """ Returns true if request params are signed by CyberSource """ signature = generate_cybersource_sa_signature(request.data) if request.data["signature"] == signature: return True else: log.error( "Cybersource signature failed: we expected %s but we got %s. Payload: %s", signature, request.data["signature"], request.data, ) return False
def test_signed_payload(self): """ A valid payload should be signed appropriately """ course_run, user = create_purchasable_course_run() order = create_unfulfilled_order(course_run.edx_course_key, user) username = '******' transaction_uuid = 'hex' now = now_in_utc() with patch('ecommerce.api.get_social_username', autospec=True, return_value=username): with patch('ecommerce.api.now_in_utc', autospec=True, return_value=now) as now_mock: with patch('ecommerce.api.uuid.uuid4', autospec=True, return_value=MagicMock(hex=transaction_uuid)): payload = generate_cybersource_sa_payload(order, 'dashboard_url') signature = payload.pop('signature') assert generate_cybersource_sa_signature(payload) == signature signed_field_names = payload['signed_field_names'].split(',') assert signed_field_names == sorted(payload.keys()) quoted_course_key = quote_plus(course_run.edx_course_key) assert payload == { 'access_key': CYBERSOURCE_ACCESS_KEY, 'amount': str(order.total_price_paid), 'consumer_id': username, 'currency': 'USD', 'item_0_code': 'course', 'item_0_name': '{}'.format(course_run.title), 'item_0_quantity': 1, 'item_0_sku': '{}'.format(course_run.edx_course_key), 'item_0_tax_amount': '0', 'item_0_unit_price': str(order.total_price_paid), 'line_item_count': 1, 'locale': 'en-us', 'override_custom_cancel_page': 'dashboard_url?status=cancel&course_key={}'.format(quoted_course_key), 'override_custom_receipt_page': "dashboard_url?status=receipt&course_key={}".format(quoted_course_key), 'reference_number': make_reference_id(order), 'profile_id': CYBERSOURCE_PROFILE_ID, 'signed_date_time': now.strftime(ISO_8601_FORMAT), 'signed_field_names': ','.join(signed_field_names), 'transaction_type': 'sale', 'transaction_uuid': transaction_uuid, 'unsigned_field_names': '', 'merchant_defined_data1': 'course', 'merchant_defined_data2': '{}'.format(course_run.title), 'merchant_defined_data3': '{}'.format(course_run.edx_course_key), } assert now_mock.called
def test_valid_signature(self): """ Signature is made up of a ordered key value list signed using HMAC 256 with a security key """ payload = { 'x': 'y', 'abc': 'def', 'key': 'value', 'signed_field_names': 'abc,x', } signature = generate_cybersource_sa_signature(payload) message = ','.join('{}={}'.format(key, payload[key]) for key in ['abc', 'x']) digest = hmac.new( CYBERSOURCE_SECURITY_KEY.encode('utf-8'), msg=message.encode('utf-8'), digestmod=hashlib.sha256, ).digest() assert b64encode(digest).decode('utf-8') == signature
def test_valid_signature(self): """ Signature is made up of a ordered key value list signed using HMAC 256 with a security key """ payload = { 'x': 'y', 'abc': 'def', 'key': 'value', 'signed_field_names': 'abc,x', } signature = generate_cybersource_sa_signature(payload) message = ','.join('{}={}'.format(key, payload[key]) for key in ['abc', 'x']) digest = hmac.new( CYBERSOURCE_SECURITY_KEY.encode('utf-8'), msg=message.encode('utf-8'), digestmod=hashlib.sha256, ).digest() assert b64encode(digest).decode('utf-8') == signature
def test_signed_payload(self): """ A valid payload should be signed appropriately """ course_run, user = create_purchasable_course_run() order = create_unfulfilled_order(course_run.edx_course_key, user) username = '******' transaction_uuid = 'hex' now = datetime.utcnow() with patch('ecommerce.api.get_social_username', autospec=True, return_value=username): with patch('ecommerce.api.datetime', autospec=True, utcnow=MagicMock(return_value=now)): with patch('ecommerce.api.uuid.uuid4', autospec=True, return_value=MagicMock(hex=transaction_uuid)): payload = generate_cybersource_sa_payload(order) signature = payload.pop('signature') assert generate_cybersource_sa_signature(payload) == signature signed_field_names = payload['signed_field_names'].split(',') assert signed_field_names == sorted(payload.keys()) assert payload == { 'access_key': CYBERSOURCE_ACCESS_KEY, 'amount': str(order.total_price_paid), 'consumer_id': username, 'currency': 'USD', 'locale': 'en-us', 'override_custom_cancel_page': 'https://micromasters.mit.edu?cancel', 'override_custom_receipt_page': "https://micromasters.mit.edu?receipt", 'reference_number': make_reference_id(order), 'profile_id': CYBERSOURCE_PROFILE_ID, 'signed_date_time': now.strftime(ISO_8601_FORMAT), 'signed_field_names': ','.join(signed_field_names), 'transaction_type': 'sale', 'transaction_uuid': transaction_uuid, 'unsigned_field_names': '', }