mvantellingen / python-zeep

A Python SOAP client
http://docs.python-zeep.org
Other
1.89k stars 586 forks source link

AttributeError: 'NoneType' object has no attribute 'find' #668

Closed gibsonxavier closed 6 years ago

gibsonxavier commented 6 years ago
  1. The version of zeep 2.5.0
  2. The WSDL you are using: https://gist.github.com/gibsonxavier/c6da760f57e062397e52400936ca5797

I am facing this issue only when using WSSE, with basic auth it is working as expected. Is this a bug or am I doing something wrong here?

session = Session() session.verify = False transport = Transport(session=session) client = Client('https://vhcals4hci.dummy.nodomain:50014?wsdl', transport=transport, wsse=Signature('pub.key', 'engine.crt')) /usr/lib/python3.5/site-packages/urllib3/connectionpool.py:858: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning) print(client.service.GetSystemInstanceList()) /usr/lib/python3.5/site-packages/urllib3/connectionpool.py:858: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning) Traceback (most recent call last): File "", line 1, in File "/usr/lib/python3.5/site-packages/zeep/client.py", line 41, in call kwargs['_soapheaders'] = soapheaders File "/usr/lib/python3.5/site-packages/zeep/wsdl/bindings/soap.py", line 121, in send return self.process_reply(client, operation_obj, response) File "/usr/lib/python3.5/site-packages/zeep/wsdl/bindings/soap.py", line 173, in process_reply message_pack = None File "/usr/lib/python3.5/site-packages/zeep/wsse/signature.py", line 62, in verify _verify_envelope_with_key(envelope, key) File "/usr/lib/python3.5/site-packages/zeep/wsse/signature.py", line 235, in _verify_envelope_with_key signature = security.find(QName(ns.DS, 'Signature')) AttributeError: 'NoneType' object has no attribute 'find

Thanks.

gibsonxavier commented 6 years ago

Adding debug logging i see :zeep.transports: HTTP Response from https://vhcals4hci.dummy.nodomain:50014/SAPControl.cgi (status: 401):

I will investigate this first on the server side and re-open this issue in case required.

Thanks.

ghost commented 6 years ago

Hi! Did you find out what the problem was? I am facing the same issues.

gibsonxavier commented 6 years ago

Hi, i had a authorisation problem on the server side as the certificate was not added correctly. Adding debug logging should help you get more information.

import logging.config

logging.config.dictConfig({
    'version': 1,
    'formatters': {
        'verbose': {
            'format': '%(name)s: %(message)s'
        }
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
    },
    'loggers': {
        'zeep.transports': {
            'level': 'DEBUG',
            'propagate': True,
            'handlers': ['console'],
        },
    }
})
kmantrala commented 1 year ago

@mvantellingen - I get this error while using zeep to make SOAP calls that need PFX certificate authentication. Our testing involves using a client certificate to authenticate for each specific web service. I had to breakdown the pfx file to extract the private key and cert in the form of pem extensions. These files were generated using openssl and legacy bindings. Something like openssl pkcs12 -in mycert.pfx -nocerts -out opensslkey.pem -nodes -legacy and `openssl pkcs12 -in mycert.pfx -nokeys -out opensslcert.pem -legacy'. Coming to the code to POST an XML this is what I did -

history = HistoryPlugin() # To enable history for the client
mySession = Session()
mySession.verify = os.getcwd()+'\opensslcert.pem'
mySession.headers = {'Content-Type': 'application/soap+xml; charset=utf-8', 
                     'User-Agent': 'Apache-HttpClient/4.5.2 (Java/17.0.7)' # Taken from ReadyAPI Raw XML which works fine.
                    }
myTransport = Transport(session=mySession)

class BinarySignatureTimestamp(BinarySignature):
    def apply(self, envelope, headers):
        security = utils.get_security_header(envelope)

        created = datetime.utcnow()
        expired = created + timedelta(seconds=50 * 60)
        print("The current CREATED UTC Time is: ",created)
        print("The current EXPIRED UTC Time is: ",expired)

        timestamp = utils.WSU('Timestamp')
        timestamp.append(utils.WSU('Created', created.replace(microsecond=0).isoformat()+'Z'))
        timestamp.append(utils.WSU('Expires', expired.replace(microsecond=0).isoformat()+'Z'))

        security.append(timestamp)

        super().apply(envelope, headers)
        return envelope, headers

# Override response verification and skip response verification for now...
# Zeep does not supprt Signature verification with different certificate...
# Ref. https://github.com/mvantellingen/python-zeep/pull/822/  "Add support for different signing and verification certificates #822"
    def verify(self, envelope):
        return envelope

consumerClient = Client(wsdl='myEndPoint.svc?wsdl', 
                        wsse=BinarySignatureTimestamp(os.getcwd()+'\opensslkey.pem', 
                                os.getcwd()+'\opensslcert.pem', 
                                '<pfx_password>'),
                        transport=myTransport,
                        plugins=[history] # To enable history for the client.
               )

print(consumerClient)

request_data = {
    'ConsumerKey':{
        'MyAlias': 'SAM', 
        'MyIdentifier': '14789765311',
        'MyCode': 'SS9'
    }
}

with consumerClient.settings(raw_response=True):
    resp = consumerClient.service.MyEndPointMethod(request_data)
print(resp)

# Printing the Request Sent and Response Received
try:
    for hist in [history.last_sent, history.last_received]:
        print("************* START PRINTING THE REQUEST(s). *************")
        print(etree.tostring(hist["envelope"], encoding="unicode", pretty_print=True))
        print("************* END PRINTING THE REQUEST(s). *************")
except (IndexError, TypeError):
    # catch cases where it fails before being put on the wire
    pass

for name,key in consumerClient.namespaces.items():
    print("The node name is: {name} and its value is {key}".format(name=name, key=key))

With this code, I get 2 errors. 1) When the endpoint method is not called because of the error indicated in the 2 point, I get

The current CREATED UTC Time is:  2023-10-18 20:37:25.313360
The current EXPIRED UTC Time is:  2023-10-18 21:27:25.313360
Traceback (most recent call last):
  File "<PythonDir>\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "<MyPythonDir>\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "..\pythonFiles\lib\python\debugpy\__main__.py", line 39, in <module>
    cli.main()
  File "..\pythonFiles\lib\python\debugpy/..\debugpy\server\cli.py", line 430, in main
    run()
  File "..\pythonFiles\lib\python\debugpy/..\debugpy\server\cli.py", line 284, in run_file
    runpy.run_path(target, run_name="__main__")
  File "..\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 321, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "..\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 135, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "..\pythonFiles\lib\python\debugpy\_vendored\pydevd\_pydevd_bundle\pydevd_runpy.py", line 124, in _run_code
  File "..\venv\lib\site-packages\zeep\proxy.py", line 46, in __call__
    return self._proxy._binding.send(
  File "..\venv\lib\site-packages\zeep\wsdl\bindings\soap.py", line 123, in send
    envelope, http_headers = self._create(
  File "..\venv\lib\site-packages\zeep\wsdl\bindings\soap.py", line 100, in _create
    envelope, http_headers = client.wsse.apply(envelope, http_headers)
  File "..\Common\soapapi_3_zeep.py", line 76, in apply
    super().apply(envelope, headers)
  File "..\venv\lib\site-packages\zeep\wsse\signature.py", line 104, in apply
    _sign_envelope_with_key_binary(
  File "..\venv\lib\site-packages\zeep\wsse\signature.py", line 287, in _sign_envelope_with_key_binary
    bintok.text = x509_data.find(QName(ns.DS, "X509Certificate")).text
AttributeError: 'NoneType' object has no attribute 'text'

As you can see, there is **### AttributeError: 'NoneType' object has no attribute 'text'** error. I really do not know why this error pops up.

2) This error actually occurs where eventhough I am able to get all the methods at the WSDL endpoint, doing any service operations (here POST), zeep errors out with the following error

<s:Body><s:Fault><s:Code><s:Value>s:Sender</s:Value><s:Subcode>
    <s:Value xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        a:InvalidSecurity
    </s:Value>
    </s:Subcode></s:Code>
    <s:Reason><s:Text xml:lang="en-US">**

> An error occurred when verifying security for the message.

**</s:Text></s:Reason>
    </s:Fault>
    </s:Body>
_I am unable to actually post any requests to the endpoint._ 

So, would you know what might be the issue and how can I resolve it?

Also, the RAW XML sent as request doesn't have the 'Content-Type' at all.

Any help is appreciated.

Thank you, -Kiran