def test_create_authentication_signature_for_post(self): # TODO change meta info to something more sensible/python-like """Tests that the to_data_to_sign function correctly constructs a POST request for multiple headers""" authenticator = DefaultAuthenticator("apiKeyId", "secretApiKey", AuthorizationType.V1HMAC) http_headers = [ RequestHeader( "X-GCS-ServerMetaInfo", "{\"platformIdentifier\":\"Windows 10/10.0.18362 Python/3.8.5 (CPython; MSC v.1916 32 bit (Intel))\"," "\"sdkIdentifier\":\"OnlinePaymentsPython3ServerSDK/v1.0.0\"}" ), RequestHeader("Content-Type", "application/json"), RequestHeader("X-GCS-ClientMetaInfo", "{\"aap\",\"noot\"}"), RequestHeader("User-Agent", "Apache-HttpClient/4.3.4 (java 1.5)"), RequestHeader("Date", "Mon, 07 Jul 2014 12:12:40 GMT") ] expected_start = "POST\n" \ "application/json\n" expected_end = "x-gcs-clientmetainfo:{\"aap\",\"noot\"}\n" \ "x-gcs-servermetainfo:{\"platformIdentifier\":\"Windows 10/10.0.18362 Python/3.8.5 (CPython; MSC v.1916 32 bit (Intel))\"," \ "\"sdkIdentifier\":\"OnlinePaymentsPython3ServerSDK/v1.0.0\"}\n" \ "/v2/1/products%20bla?aap=noot&mies=geen%20noot\n" url = urllib.parse.urlparse( "http://localhost:8080/v2/1/products%20bla?aap=noot&mies=geen%20noot" ) data_to_sign = authenticator.to_data_to_sign("POST", url, http_headers) actual_start = data_to_sign[:22] actual_end = data_to_sign[52:] self.assertEqual(expected_start, actual_start) self.assertEqual(expected_end, actual_end)
def test_constructor_with_prohibited_headers(self): """Tests that the MetaDataProvider constructor does not accept any headers marked as prohibited""" for name in MetaDataProvider.prohibited_headers: additional_headers = [RequestHeader("Header1", "Value1"), RequestHeader(name, "should be slashed and burnt"), RequestHeader("Header3", "Value3")] with self.assertRaises(Exception) as error: MetaDataProvider("OnlinePayments", None, additional_headers) self.assertIn(name, str(error.exception))
def test_unmarshal_string_success(self): helper = self.__create_helper() InMemorySecretKeyStore.INSTANCE().store_secret_key( self.__KEY_ID, self.__SECRET_KEY) body = self.__read_resource("valid-body") request_headers = [ RequestHeader(self.__SIGNATURE_HEADER, self.__SIGNATURE), RequestHeader(self.__KEY_ID_HEADER, self.__KEY_ID) ] event = helper.unmarshal(body, request_headers) self.assertEqual("v1", event.api_version) self.assertEqual("8ee793f6-4553-4749-85dc-f2ef095c5ab0", event.id) self.assertEqual("2020-01-01T12:00:00.000+0100", event.created) self.assertEqual("1", event.merchant_id) self.assertEqual("payment.paid", event.type) self.assertIsNone(event.payout) self.assertIsNone(event.refund) self.assertIsNone(event.token) self.assertIsNotNone(event.payment) self.assertEqual("1_1", event.payment.id) self.assertIsNotNone(event.payment.payment_output) self.assertIsNotNone(event.payment.payment_output.amount_of_money) self.assertEqual(1000, event.payment.payment_output.amount_of_money.amount) self.assertEqual( "EUR", event.payment.payment_output.amount_of_money.currency_code) self.assertIsNotNone( event.payment.payment_output.amount_of_money.currency_code) self.assertIsNotNone(event.payment.payment_output.references) self.assertEqual( "12345", event.payment.payment_output.references.merchant_reference) self.assertEqual("card", event.payment.payment_output.payment_method) self.assertIsNotNone( event.payment.payment_output.card_payment_method_specific_output) self.assertIsNone(event.payment.payment_output. redirect_payment_method_specific_output) self.assertIsNone(event.payment.payment_output. sepa_direct_debit_payment_method_specific_output) self.assertEqual( 1, event.payment.payment_output. card_payment_method_specific_output.payment_product_id) self.assertEqual("CAPTURED", event.payment.status) self.assertIsNotNone(event.payment.status_output) self.assertEqual(False, event.payment.status_output.is_cancellable) self.assertEqual("COMPLETED", event.payment.status_output.status_category) self.assertEqual(9, event.payment.status_output.status_code) self.assertEqual( "20200101120000", event.payment.status_output.status_code_change_date_time) self.assertEqual(True, event.payment.status_output.is_authorized)
def test_unmarshal_no_secret_key_available(self): self.clear_public_key_cache() helper = self.__create_helper() body = self.__read_resource("valid-body") request_headers = [ RequestHeader(self.__SIGNATURE_HEADER, self.__SIGNATURE), RequestHeader(self.__KEY_ID_HEADER, self.__KEY_ID) ] self.assertRaises(SecretKeyNotAvailableException, helper.unmarshal, body, request_headers)
def test_unmarshal_string_invalid_signature(self): helper = self.__create_helper() InMemorySecretKeyStore.INSTANCE().store_secret_key( self.__KEY_ID, self.__SECRET_KEY) body = self.__read_resource("valid-body") request_headers = [ RequestHeader(self.__SIGNATURE_HEADER, "1" + self.__SIGNATURE), RequestHeader(self.__KEY_ID_HEADER, self.__KEY_ID) ] self.assertRaises(SignatureValidationException, helper.unmarshal, body, request_headers)
def test_get_server_metadata_headers_additional_headers(self): """Tests that the MetaDataProvider can handle multiple additional headers""" additional_headers = [RequestHeader("Header1", "&=$%"), RequestHeader("Header2", "blah blah"), RequestHeader("Header3", "foo")] meta_data_provider = MetaDataProvider("OnlinePayments", None, additional_headers) request_headers = meta_data_provider.meta_data_headers self.assertEqual(4, len(request_headers)) for index in range(1, 4): self.assertEqual(additional_headers[index - 1].name, request_headers[index].name) self.assertEqual(additional_headers[index - 1].value, request_headers[index].value)
def test_create_simple_authentication_signature(self): """Tests if the default authenticator creates the correct signature (with an authorization type with different casing).""" authenticator = DefaultAuthenticator("apiKeyId", "secretApiKey", "v1hmac") headers = [ RequestHeader("Date", "Wed, 01 Jan 2020 11:00:00 GMT"), RequestHeader("X-GCS-ClientMetaInfo", "processed header value"), RequestHeader("X-GCS-CustomerHeader", "processed header value"), RequestHeader("X-GCS-ServerMetaInfo", "processed header value") ] url = urlparse.urlparse("http://localhost/v2/1/tokens") authentication_signature = authenticator.create_simple_authentication_signature("DELETE", url, headers) self.assertEqual("GCS v1HMAC:apiKeyId:zcDsJLRYsh99pqyCFdrVLyLVi+4A+QLis14rEtV8c98=", authentication_signature)
def test_unmarshal_api_version_mismatch(self): marshaller = mock(Marshaller) event = WebhooksEvent() event.api_version = "v0" body = self.__read_resource("valid-body") when(marshaller).unmarshal(body, WebhooksEvent).thenReturn(event) helper = self.__create_helper(marshaller) InMemorySecretKeyStore.INSTANCE().store_secret_key( self.__KEY_ID, self.__SECRET_KEY) request_headers = [ RequestHeader(self.__SIGNATURE_HEADER, self.__SIGNATURE), RequestHeader(self.__KEY_ID_HEADER, self.__KEY_ID) ] self.assertRaises(ApiVersionMismatchException, helper.unmarshal, body, request_headers)
def to_data_to_sign(self, http_method, resource_uri, http_headers): content_type = None date = None canonicalized_headers = "" canonicalized_resource = self.__to_canonicalized_resource(resource_uri) xgcs_http_headers = [] if http_headers is not None: for http_header in http_headers: if "Content-Type".lower() == http_header.name.lower(): content_type = http_header.value elif "Date".lower() == http_header.name.lower(): date = http_header.value else: name = self.__to_canonicalize_header_name(http_header.name) if name.startswith("x-gcs"): value = self.to_canonicalize_header_value( http_header.value) xgcs_http_header = RequestHeader(name, value) xgcs_http_headers.append(xgcs_http_header) xgcs_http_headers.sort(key=attrgetter('name')) for xgcs_http_header in xgcs_http_headers: canonicalized_headers += xgcs_http_header.name + ":" + xgcs_http_header.value + "\n" string = http_method.upper() + "\n" if content_type is not None: string += content_type + "\n" else: string += "\n" string += date + "\n" string += str(canonicalized_headers) string += canonicalized_resource + "\n" return str(string)
def test_get_header_list(self): """Tests get_header using a list of RequestHeader objects""" headers = [RequestHeader("Content-Type", "application/json")] self.assertEqual("Content-Type:application/json", str(get_header(headers, "Content-Type"))) self.assertEqual("Content-Type:application/json", str(get_header(headers, "content-type"))) self.assertIsNone(get_header_value(headers, "Content-Length"))
def _client_headers(self): if self._client_meta_info is not None: client_headers = [ RequestHeader("X-GCS-ClientMetaInfo", self._client_meta_info) ] return client_headers else: return None
def assertClientHeaders(self, client, client_meta_info): """Checks that the 'ClientMetaInfo' header with client_meta_info is stored properly in the client""" headers = client._client_headers header_value = base64.b64encode(client_meta_info.encode("utf-8")) expected = RequestHeader("X-GCS-ClientMetaInfo", header_value) found = False for header in headers: if str(expected) == str(header): found = True self.assertTrue( found, "header {0} was not found in {1}".format(expected, headers))
def _add_generic_headers(self, http_method, uri, request_headers, context): """ Adds the necessary headers to the given list of headers. This includes the authorization header, which uses other headers, so when you need to override this method, make sure to call super.addGenericHeaders at the end of your overridden method. """ # add server meta info header request_headers.extend(self.meta_data_provider.meta_data_headers) # add date header request_headers.append( RequestHeader("Date", self._get_header_date_string())) if context is not None and context.idempotence_key is not None: # add context specific headers request_headers.append( RequestHeader("X-GCS-Idempotence-Key", context.idempotence_key)) # add signature authentication_signature = \ self.authenticator.create_simple_authentication_signature(http_method, uri, request_headers) request_headers.append( RequestHeader("Authorization", authentication_signature))
def __init__(self, integrator, shopping_cart_extension=None, additional_request_headers=()): MetaDataProvider.__validate_additional_request_headers(additional_request_headers) def subber(name_or_value): return re.sub(r'\r?\n(?:(?![\r\n])\s)*', " ", name_or_value).strip() additional_request_headers = [RequestHeader(subber(header.name), subber(header.value)) for header in additional_request_headers] server_meta_info = self.ServerMetaInfo() server_meta_info.platform_identifier = self._platform_identifier server_meta_info.sdk_identifier = self._sdk_identifier server_meta_info.sdk_creator = "OnlinePayments" server_meta_info.integrator = integrator server_meta_info.shopping_cart_extension = shopping_cart_extension server_meta_info_string = DefaultMarshaller.INSTANCE().marshal(server_meta_info) server_meta_info_header = RequestHeader(self.__SERVER_META_INFO_HEADER, b64encode(server_meta_info_string.encode('utf-8'))) if not additional_request_headers: self.__meta_data_headers = tuple([server_meta_info_header]) else: request_headers = [server_meta_info_header] request_headers.extend(additional_request_headers) self.__meta_data_headers = tuple(request_headers)
def _put(self, relative_path, request_headers, request_parameters, request_body, context): if request_parameters is None: request_parameter_list = None else: request_parameter_list = request_parameters.to_request_parameters() uri = self._to_absolute_uri(relative_path, request_parameter_list) if request_headers is None: request_headers = [] body = None if request_body is not None: request_headers.append( RequestHeader("Content-Type", "application/json")) body = self.__marshaller.marshal(request_body) self._add_generic_headers("PUT", uri, request_headers, context) return self.connection.put(uri, request_headers, body)