Python Zeep - Multiple WSDL Files - python

I have two separate WSDL files that are provided to me to interact with a service, one WSDL file just provides a method to login and generate an access token. The other WSDL file provides the methods to actually interact with the system.
If I instantiate the zeep SOAP client with the first WSDL file to login do I need to reinstantiate the client for the next WSDL file or can I simply tell it to go look at the next WSDL file?
from zeep import Client
client = Client("https://url.service.com/Session?wsdl")
token = client.service.login(username, password)
client = Client("https://url.service.com/Object?wsdl")
client.service.find(token, 'filter')
I attempted to use create_service but I don't think I'm using it correctly.
Thank you!

You need to reinstantiate the second Client.
I expect that you also need to extend your code to use the same requests Session and Zeeps Transport.
from requests import Session
from zeep import Client
from zeep.transports import Transport
transport = Transport(session=Session())
client = Client("https://url.service.com/Session?wsdl", transport=transport)
token = client.service.login(username, password)
client = Client("https://url.service.com/Object?wsdl", transport=transport)
client.service.find(token, 'filter')

Related

zeep client exception. zeep.transport.session.cookies not propagating

I tried to create a zeep client and use it by two methods.
1) I tried to keep everything in a single module . i created the zeep client object and it was working fine while using a payload.
2) I created a method which returns a zeep client object for a wsdl. I tried to use this a way as method 1) But getting the below error.
zeep.exception.Fault : Incoming message could not be authenticated. No valid credentials found
Can someone please advise what I am missing here which causes this error. My second approach is like this.
\\
def zeepClient(wsdl):
## do all here and return zeep client object.
return client
#Now in another module I do call that above method like this
Client=othermodule.zeepClient(mywsdl)
Payload={my payload}
Client.service.myservice(**Payload)
\\
If I do this , I get above error.But if my above piece of code and my method for zeepClient are all in same place. I am not getting error.
Not sure . What that Returned Client object is missing.
You must provide credentials to the session and then make a request like here
from requests import Session
from requests.auth import HTTPBasicAuth # or HTTPDigestAuth, or OAuth1, etc.
from zeep import Client
from zeep.transports import Transport
session = Session()
session.auth = HTTPBasicAuth(user, password)
client = Client('http://my-endpoint.com/production.svc?wsdl',
transport=Transport(session=session))

Zeep with namespace(?) variables

I have an XML/WCF API I need to implement something against. The API client library is only provided as c# in Windows and our company does not do either c# or Windows. I am now experimenting with Python and zeep. The api is Symmetry access control system API if anyone is interested.
I can connect to the server and read the wsdl structure. This works:
URL='https://localhost/smsXMLWebService/SMSXMLWebService.svc?singleWsdl'
URL2='https://localhost/smsXMLWebService/smsXMLWebService.svc'
session = Session()
session.verify = False
transport = Transport(session=session)
self.client = zeep.Client(URL, transport=transport)
self.service = self.client.create_service('{http://tempuri.org/}WSHttpBinding_ISMSXMLWebService', URL2)
Now everything from that point forward will require login to the platform. In the example c# code this is done as follows:
G4TAPI = new SMSXMLWebServiceClient();
G4TAPI.ClientCredentials.UserName.UserName = txtUserName.Text
G4TAPI.ClientCredentials.UserName.Password = txtPassword.Text.ToLower();
G4TAPI.G4TLogin();
My self.service has now G4TLogin() call and it seems to attempt to connect when I wireshark the traffic. But how do I set the username and password as they are not given as parameters to G4TLogin() method?
This does not work:
self.service.ClientCredentials.UserName.UserName = "api"
This is very much out of my comfort zone and I may be using incorrect terminology here. Any ideas?
The error message is
AttributeError: Service has no operation 'ClientCredentials'
When using Zeep make sure to study the namespaces in WSDL URL, using
python -mzeep "YourWsdlUrlGoesHere"
Get the parameters and make a Python dictionary from them (in my case facing C#, including username and password in the dictionary) note that one might need to make nested dictionary like in my case.
from requests import Session
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
from zeep import Client
from zeep.transports import Transport
request = { "Credential":{"Username": "yourusername",
"Password": "yourpassword"},
"RN": "150147119"
}
session = Session()
client = Client('http://your url and wsdl../Invoice.svc?Wsdl',transport=Transport(session=session))
r = client.service.NameOfYourService(request)
print(r)
Do not pass user and password in Zeep formal format. Passing the dictionary worked for me.
In my case the WSDL suggested user and password be in credential and a string be passed in RN and finally all be passed in a one variable.

How to comply with policy defined in WSDL

I'm trying to access a SOAP web service using Zeep
There is a publicly available WSDL, and a testing WSDL (has a self-signed cert)
My code to test (from test site) is:
from requests import Session
from zeep import Client
from zeep.transports import Transport
from zeep.wsse.username import UsernameToken
import xml.dom.minidom
WS_USER_NAME = '<username>'
WS_PASSWORD = '<password>'
WS_WSDL = 'https://pre.ipddb.org/WS/Services/IpdDownloadService.svc?wsdl'
session = Session()
session.verify = False
transport = Transport(session=session,
operation_timeout=10)
client = Client(wsdl=WS_WSDL,
wsse=UsernameToken(WS_USER_NAME, WS_PASSWORD),
transport=transport)
with client.options(raw_response=True):
response = client.service.Search(strNames='Rick Astley')
xml = xml.dom.minidom.parseString(response._content)
print xml.toprettyxml()
My response comes back as:
<?xml version="1.0" ?>
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/soap/fault</a:Action>
<a:RelatesTo>urn:uuid:5ffbeb15-913b-41ec-a2ef-556c131c07eb</a:RelatesTo>
</s:Header>
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Sender</s:Value>
<s:Subcode>
<s:Value xmlns:a="http://schemas.xmlsoap.org/ws/2005/02/sc">a:BadContextToken</s:Value>
</s:Subcode>
</s:Code>
<s:Reason>
<s:Text xml:lang="es-ES">The message could not be processed. This is most likely because the action 'https://www.ipddb.org/ws/IpdDownloadService/Search' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding.</s:Text>
</s:Reason>
</s:Fault>
</s:Body>
</s:Envelope>
I was provided a username and password from the owners of the web service, so I know I'll need to provide that, but there must be something else I'm missing. I believe it has to do with the policy defined in the WSDL, but the web service doesn't provide anything in terms of documentation.
I'm new to SOAP, but is there enough in the WSDL for me to figure what they need to comply with policy?
Will I be able to use Zeep to fulfill all the policies?
Do I need more info from those maintaining the web service?
Not that it's a direct answer to your question, but when I have to make a soap request I like to familiarize myself with the API by using soapui.
Once you provide a WSDL for soapui, it will automatically generate all of the required parameters for a proper request. By doing so, you can validate that the error you're receiving is because of a system policy and not because of zeep.

Sharing a SOAP client object between Django views

My Django application uses an API which is a SOAP service. I'm currently using a python SOAP/WSDL client called SUDS. In order to use the API, I have to initialize the client and provide it with a link to the endpoint, login, API key, and password.
Simplified code that does it:
from suds.client import Client
client = Client('link.to.WSDL.endpoint')
session_id = client.service.a_log_in_service('api_key', 'login', 'password')
# the session_id is required by all other 'client.service.xxx' services
Everything works just fine, although it takes some time to initialize the client.
Now, every time I want to send a request to the API in a view, I have to initialize the client. I can't see any way to "share" the already initialized client among many views. It would be great to have the client up and running somewhere, so that any user can send an API request using it.
Once I've done the log in in a view, I can store the session_id (in a session, db, file) and use it in other views, but the client still needs to be initialized over again, which means downloading the 200-KB XML file.
Having to download 200 KB every time a user hits a button seems not right at all.
Thanks in advance!

accessing client's x509 certificate from within twisted web WSGI app

I have set up a twisted + flask https server that also does certificate-based client authentication by following the documentation at the Twisted site here. So far, so good.
In addition to authenticating the client using a certificate, the application code within the flask app needs the user name (which is present in the client x509 certificate) in order to do its job. I couldn't find an easy way to access this information. The information (based on the documentation) seems to be in the pyopenssl X509Name object at the time it does authentication, and I need the identity at the flask layer every time I process a request from that client.
The request object flask is getting did not seem to have this information (unless I read it wrong), so I assume I need to modify some options at the Twisted level to send them through to flask. I also need to somehow get them out of the OpenSSL layer.
How would you do this?
Updated: using HTTPChannel.allHeadersReceived instead of Protocol.dataReceived for support of chunked requests.
You can use HTTP headers to store connection information: set them up in HTTPChannel.allHeadersReceived method and retrieve from flask.request.headers, e.g.:
from twisted.application import internet, service
from twisted.internet import reactor
from twisted.web.http import HTTPChannel
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
from flask import Flask, request
app = Flask('app')
#app.route('/')
def index():
return 'User ID: %s' % request.headers['X-User-Id']
class MyHTTPChannel(HTTPChannel):
def allHeadersReceived(self):
user_id = 'my_user_id'
req = self.requests[-1]
req.requestHeaders.addRawHeader('X-User-Id', user_id)
HTTPChannel.allHeadersReceived(self)
class MySite(Site):
protocol = MyHTTPChannel
application = service.Application('myapplication')
service = service.IServiceCollection(application)
http_resource = WSGIResource(reactor, reactor.getThreadPool(), app)
http_site = MySite(http_resource)
internet.TCPServer(8008, http_site).setServiceParent(service)
I'm not familiar with using client certificates in twisted. I assume you can retrieve its information in Protocol.transport.

Categories

Resources