# Copyright (C) 2021 Entr'ouvert # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import base64 import urllib.parse import pytest from passerelle.apps.soap.models import SOAPConnector from . import utils pytestmark = pytest.mark.django_db class SOAP11: VERSION = '1.1' ENDPOINT_URL = 'https://www.examples.com/SayHello/' WSDL_CONTENT = '''\ WSDL File for HelloService ''' WSDL_URL = 'https://example.com/service.wsdl' SOAP_RESPONSE = '''\ Hello John! ''' INPUT_SCHEMA = { 'properties': { 'firstName': { 'description': '{http://www.examples.com/wsdl/HelloService.wsdl}firstName', 'properties': { 'string': {'items': {'type': 'string', 'description': 'xsd:string'}, 'type': 'array'}, }, 'required': ['string'], 'type': 'object', }, 'lastName': {'type': 'string', 'description': 'xsd:string'}, }, 'required': ['firstName', 'lastName'], 'type': 'object', } OUTPUT_SCHEMA = { 'properties': { 'greeting': {'type': 'string', 'description': 'xsd:string'}, 'who': {'type': 'string', 'description': 'xsd:string'}, }, 'required': ['greeting', 'who'], 'type': 'object', } INPUT_DATA = { 'firstName/string/0': 'John', 'firstName/string/1': 'Bill', 'lastName': 'Doe', } OUTPUT_DATA = { 'greeting': 'Hello', 'who': 'John!', } VALIDATION_ERROR = 'Missing element firstName (sayHello.firstName)' class SOAP12(SOAP11): VERSION = '1.2' ENDPOINT_URL = 'https://www.examples.com/SayHello/' WSDL_CONTENT = f'''\ ''' SOAP_RESPONSE = '''\ Hello John! ''' INPUT_SCHEMA = { 'type': 'object', 'properties': { 'firstName': {'type': 'array', 'items': {'type': 'string', 'description': 'xsd:string'}}, 'lastName': {'type': 'string', 'description': 'xsd:string'}, }, 'required': ['firstName', 'lastName'], 'description': '{urn:examples:helloservice}sayHello', } OUTPUT_SCHEMA = { 'description': '{urn:examples:helloservice}sayHelloResponse', 'properties': { 'greeting': {'type': 'string', 'description': 'xsd:string'}, 'who': { 'type': 'array', 'items': {'type': 'string', 'description': 'xsd:string'}, }, }, 'required': ['greeting', 'who'], 'type': 'object', } INPUT_DATA = { 'firstName/0': 'John', 'firstName/1': 'Bill', 'lastName': 'Doe', } OUTPUT_DATA = { 'greeting': 'Hello', 'who': ['John!'], } VALIDATION_ERROR = 'Expected at least 1 items (minOccurs check) 0 items found. (sayHello.firstName)' class BrokenSOAP12(SOAP12): WSDL_CONTENT = SOAP12.WSDL_CONTENT[-100:] # truncate the WSDL to break it @pytest.fixture(params=[SOAP11, SOAP12]) def soap(request): p = request.param() with utils.mock_url(p.WSDL_URL, response=p.WSDL_CONTENT): with utils.mock_url(p.ENDPOINT_URL, response=p.SOAP_RESPONSE) as mock: p.endpoint_mock = mock yield p class TestManage: @pytest.fixture def app(self, app, admin_user): from .test_manager import login login(app) return app def test_homepage(self, app, connector, soap): response = app.get(f'/soap/{connector.slug}/') assert 'Method sayHello' in response @pytest.mark.parametrize('soap', [BrokenSOAP12], indirect=True) def test_homepage_broken_wsdl(self, app, connector, soap): response = app.get(f'/soap/{connector.slug}/') response = app.get(f'/soap/{connector.slug}/') assert response.pyquery('.down') @pytest.fixture def connector(db, soap): return utils.setup_access_rights( SOAPConnector.objects.create( slug='test', wsdl_url=soap.WSDL_URL, zeep_strict=True, zeep_xsd_ignore_sequence_order=False ) ) def test_schemas(connector, soap): assert list(connector.operations_and_schemas) == [('sayHello', soap.INPUT_SCHEMA, soap.OUTPUT_SCHEMA)] def test_say_hello_method_validation_error(connector, soap, app): resp = app.get('/soap/test/method/sayHello/') assert resp.json == { "err": 1, "err_class": "passerelle.utils.soap.SOAPError", "err_desc": soap.VALIDATION_ERROR, "data": None, } def test_say_hello_method_ok_get(connector, app, caplog, soap): resp = app.get('/soap/test/method/sayHello/?' + urllib.parse.urlencode(soap.INPUT_DATA)) assert '>John<' in soap.endpoint_mock.handlers[0].call['requests'][-1].body.decode() assert '>Bill<' in soap.endpoint_mock.handlers[0].call['requests'][-1].body.decode() assert '>Doe<' in soap.endpoint_mock.handlers[0].call['requests'][-1].body.decode() assert resp.json == {'data': soap.OUTPUT_DATA, 'err': 0} def test_say_hello_method_ok_get_with_signature(connector, app, caplog, soap): resp = app.get('/soap/test/method/sayHello/?' + urllib.parse.urlencode(soap.INPUT_DATA) + '&signature=1') assert resp.json['err'] == 0 def test_say_hello_method_ok_post_json(connector, app, caplog, soap): resp = app.post_json('/soap/test/method/sayHello/', params=soap.INPUT_DATA) assert '>John<' in soap.endpoint_mock.handlers[0].call['requests'][-1].body.decode() assert '>Bill<' in soap.endpoint_mock.handlers[0].call['requests'][-1].body.decode() assert '>Doe<' in soap.endpoint_mock.handlers[0].call['requests'][-1].body.decode() assert resp.json == {'data': soap.OUTPUT_DATA, 'err': 0} @pytest.mark.parametrize('soap', [SOAP12], indirect=True) class TestAuthencation: def test_basic_auth(self, connector, app, caplog, soap): connector.basic_auth_username = 'username' connector.basic_auth_password = 'password' connector.save() app.post_json('/soap/test/method/sayHello/', params=soap.INPUT_DATA) assert ( base64.b64decode( soap.endpoint_mock.handlers[0].call['requests'][1].headers['Authorization'].split()[1] ) == b'username:password' ) assert b'wsse:UsernameToken' not in soap.endpoint_mock.handlers[0].call['requests'][1].body def test_username_token(self, connector, app, caplog, soap): connector.zeep_wsse_username = 'username' connector.zeep_wsse_password = 'password' connector.save() app.post_json('/soap/test/method/sayHello/', params=soap.INPUT_DATA) assert 'Authorization' not in soap.endpoint_mock.handlers[0].call['requests'][1].headers assert b'wsse:UsernameToken' in soap.endpoint_mock.handlers[0].call['requests'][1].body