Python SOAP client, WSDL call with suds gives Transport Error 401 Unauthorized for HTTP basic authentication
Asked Answered
C

2

10

Background

I'm building a SOAP client with python 2.7.3 and using the suds 0.4.1 library provided by Canonical. The server is using basic authentication over HTTPS.

Problem

Can't pass authentication on the server, even to get at the WSDL. I get the following error:

suds.transport.TransportError: HTTP Error 401: Unauthorized

Attempts at resolution and code

I have tried both of the authentication methods described in the suds documentation, but still get the error above at the client = Client(url, ...) line. I've confirmed the credentials and ability to connect in a web browser, which works fine.

After declaring wsdl_url, username and password, I tried:

client = Client(url=wsdl_url, username=username, password=password)

# as well as:

t = HttpAuthenticated(username=username, password=password)
client = Client(url=wsdl_url, transport=t)

# and even:

t = HttpAuthenticated(username=username, password=password)
t.handler = urllib2.HTTPBasicAuthHandler(t.pm)
t.urlopener = urllib2.build_opener(t.handler)
client = Client(url=wsdl_url, transport=t)

That last one seems to, at least, get a response from the WSDL URL in another question about HTTP authentication with suds.

Other notes

This question is distinct from this similar question because I am using:

from suds.transport.https import HttpAuthenticated
# not:
# from suds.transport.http import HttpAuthenticated

and from the Traceback, the client = Client(url, ...) call clearly hits suds.transport.https.py:

File "/usr/lib/python2.7/dist-packages/suds/client.py", line 112, in __init__ self.wsdl = reader.open(url)
File "/usr/lib/python2.7/dist-packages/suds/reader.py", line 152, in open d = self.fn(url, self.options)
File "/usr/lib/python2.7/dist-packages/suds/wsdl.py", line 136, in __init__ d = reader.open(url)
File "/usr/lib/python2.7/dist-packages/suds/reader.py", line 79, in open d = self.download(url)
File "/usr/lib/python2.7/dist-packages/suds/reader.py", line 95, in download fp = self.options.transport.open(Request(url))
File "/usr/lib/python2.7/dist-packages/suds/transport/https.py", line 60, in open return  HttpTransport.open(self, request)
File "/usr/lib/python2.7/dist-packages/suds/transport/http.py", line 64, in open raise TransportError(str(e), e.code, e.fp)

What am I missing?

Conflagration answered 31/7, 2012 at 14:31 Comment(2)
+1 for well explained question. could we have a look at the full traceback? also, sorry if this sound patronising, but are you sure you are using the correct username & password for the serviceDeliver
@Deliver I added more of the traceback. The preceding line will vary depending on which version of the client = Client(url=wsdl_url ...) I use. I was convinced it had to be a mistake with the username and password, too, but I copy-and-pasted straight from the config file into the browser (multiple times!) to make absolutely sure.Conflagration
C
11

suds wasn't adding the authorization header to the request, so I set it manually:

import base64

# code excluded for brevity

base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
authenticationHeader = {
    "SOAPAction" : "ActionName",
    "Authorization" : "Basic %s" % base64string
}
client = Client(url=wsdl_url, headers=authenticationHeader)
Conflagration answered 28/8, 2012 at 16:0 Comment(2)
I'm getting the exact same 401: Unauthorized issue using this method. :(Induplicate
Not sure why this was selected as an answer. I'm still facing exact same 401:Unauthorized error, even after base64 encoding: #63545953 as also mentioned by user @InduplicateStrigil
D
0

You can used SOAPpy

import SOAPpy, base64

class myHTTPTransport(SOAPpy.HTTPTransport):
    username = None
    passwd = None

    @classmethod
    def setAuthentication(cls,u,p):
        cls.username = u
        cls.passwd = p

    def call(self, addr, data, namespace, soapaction=None, encoding=None, http_proxy=None, config=SOAPpy.Config, timeout=None):

        if not isinstance(addr, SOAPpy.SOAPAddress):
          addr = SOAPpy.SOAPAddress(addr, config)

        if self.username != None:
          addr.user = self.username+":"+self.passwd

        return SOAPpy.HTTPTransport.call(self, addr, data, namespace, soapaction, encoding, http_proxy, config)


if __name__ == '__main__':

  # code for authenticating the SOAP API calls
  myHTTPTransport.setAuthentication('admin', 'admin')

  # Getting the instance of Server
  Baton = SOAPpy.WSDL.Proxy(<WSDL PATH>, transport=myHTTPTransport)

  # your code goes here
  ...
Dearth answered 13/2, 2014 at 18:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.