Python SOAP client with Zeep - authentication - python

I am trying to use Zeep to implement a SOAP client, as it seems the only maintained library at the moment:
ZSI looked very good but its latest version on pypi dates 2006
suds seemed to be a popular alternative, but the master is unmaintained since 2011 and there are a lot of forks out there but none seems "official" and "recent" enough to be used in a large project.
So, trying to use Zeep, I am stuck with the authentication required by the server to access the WSDL.
Such operation was quite easy with ZSI:
from ZSI.client import Binding
from ZSI.auth import AUTH
b = Binding(url='http://mysite.dom/services/MyWebServices?WSDL')
b.SetAuth(AUTH.httpbasic, 'userid', 'password')
and I can find something similar in __main__.py of Zeep:
from six.moves.urllib.parse import urlparse
from zeep.cache import InMemoryCache, SqliteCache
from zeep.client import Client
from zeep.transports import Transport
cache = SqliteCache() if args.cache else InMemoryCache()
transport_kwargs = {'cache': cache}
result = urlparse(args.wsdl_file)
if result.username or result.password:
transport_kwargs['http_auth'] = (result.username, result.password)
transport = Transport(**transport_kwargs)
client = Client(args.wsdl_file, transport=transport)
but that does not work in my case, I get an error:
Exception: HTTPConnectionPool(host='schemas.xmlsoap.org', port=80): Max retries exceeded with url: /soap/encoding/ (Caused by NewConnectionError('<requests.packages.urllib3.connection.HTTPConnection object at 0x7f3dab9d30b8>: Failed to establish a new connection: [Errno 110] Connection timed out',))

Probably with the newer Version of zeep the older solution does not work anymore. Here is the new way:
from requests.auth import HTTPBasicAuth # or HTTPDigestAuth, or OAuth1, etc.
from requests import Session
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))

For Basic Access Authentication you can use the HTTPBasicAuth class from the requests module, as explained on Zeep documentation http://docs.python-zeep.org/en/master/transport.html:
from requests.auth import HTTPBasicAuth # or HTTPDigestAuth, or OAuth1, etc.
from zeep import Client
from zeep.transports import Transport
client = Client('http://my-endpoint.com/production.svc?wsdl',
transport=Transport(http_auth=HTTPBasicAuth(user, password)))

In my case the API I was working with required WS-Security (WSSE) rather than HTTP.
from zeep import Client
from zeep.wsse.username import UsernameToken
client = Client(<wsdl_url>, wsse=UsernameToken(<username>, <password>)

Related

Python zeep. Force not to use a Proxy

How can i use python zeep module and configure the connection to connect with no proxy?
I need to access an internal WSDL. That means no proxy is needed.
I have tried to create the client:
from zeep import client
client = Client("myURL")
But i am getting an error because is trying to connect with a default proxy
Regards.
Using the information provided in the links provided by Dima,
the following worked for me:
session = requests.Session()
session.trust_env = False
transport = Transport(timeout=10)
transport.session = session
client = Client("your url", transport=transport)

How to use AWS certificate with python requests library?

I have an elastic load balancer running on aws cloud. I have attached a HTTPS listener to it on port 443, and it is using a certificate in certificate manager. I want to send https requests from a python script to the elastic load. I can't figure out the exact api call that I should make to implement this.
You can make https get request to the DNS of the ELB.
To generate HTTPS request use following script quoted from this answer.
import urllib.request
r = urllib.request.urlopen('<DNS OF ELB>')
print(r.read())
If you really want to use http.client, you must call endheaders
after you send the request headers:
import http.client
conn = http.client.HTTPSConnection('DNS OF ELB', 443)
conn.putrequest('GET', '/')
conn.endheaders() # <---
r = conn.getresponse()
print(r.read())
As a shortcut to putrequest/endheaders, you can also use the request
method, like this:
import http.client
conn = http.client.HTTPSConnection('DOMAIN', 443)
conn.request('GET', '/') # <---
r = conn.getresponse()
print(r.read())
Update 1
For Python 2.7 You can use httplib or urllib2
If you are using httplib, HTTPS supports only if the socket module was compiled with SSL support.
For urllib2 refer this article.

Reading SOAP with Zeep

I'm trying to read this Soap API 'https://www.shab.ch/soapserver' using Python's Zeep.
from requests import Session
from requests.auth import HTTPBasicAuth
from zeep import Client
from zeep.transports import Transport
session = Session()
session.auth = HTTPBasicAuth('MYUSERNAME', 'MYPASSWORD')
client = Client('https://www.shab.ch/shabforms/acmsservice?wsdl',
transport=Transport(session=session))
But get the following error message:
HTTPError: 403 Client Error: Forbidden for url:
https://www.shab.ch/shabforms/acmsservice?wsdl
Can anybody see what I am doing wrongly here?
It doesn't seem that Python is your problem. Instead you have a rights issue for the WSDL file. A quick Googling turned up:
https://www.shab.ch/soapserver
https://www.shab.ch/shabforms/soapserver?wsdl
You don't have permission to go to that part of the Shab.ch server. You need a username and password. Contact: info#sogc.ch

Python SOAP client with Zeep - import namespace

A little context: I am opening this question arose here, after solving an authentication problem. I prefer to open a new one to avoid polluting the previous with comments not related to the original issue, and to give it the proper visibility.
I am working on a SOAP client running in the same intranet as the server, without internet access.
from requests.auth import HTTPBasicAuth
from zeep import Client
from zeep.transports import Transport
wsdl = 'http://mysite.dom/services/MyWebServices?WSDL'
client = Client(wsdl, transport=HTTPBasicAuth('user','pass'), cache=None)
The problem: WSDL contains an import to an external resource located outside the intranet ('import namespace="schemas.xmlsoap.org/soap/encoding/"') and therefore Zeep Client instantiation fails with:
Exception: HTTPConnectionPool(host='schemas.xmlsoap.org', port=80): Max retries exceeded with url: /soap/encoding/ (Caused by NewConnectionError('<requests.packages.urllib3.connection.HTTPConnection object at 0x7f3dab9d30b8>: Failed to establish a new connection: [Errno 110] Connection timed out',))
Question: is it possible (and does it make sense) to create the Zeep Client without accessing the external resource?
As an additional detail, another client written in Java, based on XML rpc ServiceFactory seems to be more resilient to this kind of problem, the service is created (and works) even if no internet connection is available.
Is it really needed to import the namespace from xmlsoap.org?
Edit, after answer from #mvt:
So, I went for the proposed solution, which allows me at the same time to control the access to external resources (read: forbid access to servers different from the one hosting the endpoint).
class MyTransport(zeep.Transport):
def load(self, url):
if not url:
raise ValueError("No url given to load")
parsed_url = urlparse(url)
if parsed_url.scheme in ('http', 'https'):
if parsed_url.netloc == "myserver.ext":
response = self.session.get(url, timeout=self.load_timeout)
response.raise_for_status()
return response.content
elif url == "http://schemas.xmlsoap.org/soap/encoding/":
url = "/some/path/myfile.xsd"
else:
raise
elif parsed_url.scheme == 'file':
if url.startswith('file://'):
url = url[7:]
with open(os.path.expanduser(url), 'rb') as fh:
return fh.read()
I would suggest doing your custom overriding of the URL and calling load() from the super class. This way if the super class code changes (which it has), you would not need to refactor your CustomTransport class.
from zeep.transports import Transport
class CustomTransport(Transport):
def load(self, url):
# Custom URL overriding to local file storage
if url and url == "http://schemas.xmlsoap.org/soap/encoding/":
url = "/path/to/schemas.xmlsoap.org.xsd"
# Call zeep.transports.Transport's load()
return super(CustomTransport, self).load(url)
The way to use the Transports in zeep is described here, but here is a quick example of using the CustomTransport:
from requests import Session
from requests.auth import HTTPBasicAuth
from zeep import Client
session = Session()
client = Client('http://example.com/production.svc?wsdl', transport=CustomTransport(session=session))
client.service.foo()
You could create your own subclass of the tranport class and add additional logic to the load() method so that specific url's are redirected / loaded from the filesystem.
The code is pretty easy i think: https://github.com/mvantellingen/python-zeep/blob/master/src/zeep/transports.py :-)

403 Forbidden Error for Python-Suds contacting Sharepoint

I'm using Python's SUDs lib to access Sharepoint web services.
I followed the standard doc from Suds's website.
For the past 2 days, no matter which service I access, the remote service always returns 403 Forbidden.
I'm using Suds 0.4 so it has built-in support for accessing Python NTLM.
Let me know if anyone has a clue about this.
from suds import transport
from suds import client
from suds.transport.https import WindowsHttpAuthenticated
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
ntlm = WindowsHttpAuthenticated(username='USER_ID', password='PASS')
c_lists = client.Client(url='https://SHAREPOINT_URL/_vti_bin/Lists.asmx?WSDL', transport=ntlm)
#c_lists = client.Client(url='https://SHAREPOINT_URL/_vti_bin/spsearch.asmx?WSDL')
#print c_lists
listsCollection = c_lists.service.GetListCollection()
Are you specifying the username as DOMAIN\USER_ID as indicated in examples for the python-ntlm library? (Also see this answer).

Categories