python - Suds sobre https con cert
ssl (5)
Basado en la respuesta de @k4ml, solo he agregado el open()
que permite buscar el WSDL usando el certificado.
Este método debería corregir el suds.transport.TransportError: HTTP Error 403: Forbidden
cuando se intenta obtener un WSDL (en la creación del Cliente) servido detrás de un servicio HTTPS.
import requests
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError
class RequestsTransport(HttpAuthenticated):
def __init__(self, **kwargs):
self.cert = kwargs.pop(''cert'', None)
# super won''t work because not using new style class
HttpAuthenticated.__init__(self, **kwargs)
def open(self, request):
"""
Fetches the WSDL using cert.
"""
self.addcredentials(request)
resp = requests.get(request.url, data=request.message,
headers=request.headers, cert=self.cert)
result = io.StringIO(resp.content.decode(''utf-8''))
return result
def send(self, request):
"""
Posts to service using cert.
"""
self.addcredentials(request)
resp = requests.post(request.url, data=request.message,
headers=request.headers, cert=self.cert)
result = Reply(resp.status_code, resp.headers, resp.content)
return result
Nota al margen, también hice una edición sugerida a la respuesta de k4ml, pero puede tomar años antes de que se apruebe.
Tengo el servicio de jabón en Apache con SSL, la espuma funciona mejor sin SSL.
Tengo certificado de cliente (archivos my.crt y user.p12).
¿Cómo debo configurar el cliente de suds para que funcione con el servicio en https?
sin certs veo
urllib2.URLError: <urlopen error [Errno 1] _ssl.c:499: error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure>
Extendiendo la solución @ k4ml, usando cert + key Esto resolverá excepciones como:
requests.exceptions.SSLError: [SSL] PEM lib (_ssl.c:2599)
Solución:
import requests
from suds.client import Client
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError
class RequestsTransport(HttpAuthenticated):
def __init__(self, **kwargs):
self.cert = kwargs.pop(''cert'', None)
HttpAuthenticated.__init__(self, **kwargs)
def send(self, request):
self.addcredentials(request)
resp = requests.post(
request.url,
data=request.message,
headers=request.headers,
cert=self.cert,
verify=True
)
result = Reply(resp.status_code, resp.headers, resp.content)
return result
t = RequestsTransport(cert=(''<your cert.pem path>'', ''your key.pem path''))
headers = {"Content-Type": "text/xml;charset=UTF-8", "SOAPAction": ""}
client = Client(wsdl_url, headers=headers, transport=t)
La característica de seguridad SSL se habilita automáticamente en python 2.7.9+, lo que rompe las jabonaduras y otras bibliotecas de Python. Estoy compartiendo un parche que puede solucionarlo:
Ubica la biblioteca de suds y reemplaza la función de u2handlers en el archivo suds / trasnport / http.py con la siguiente línea:
import ssl
def u2handlers(self):
"""
Get a collection of urllib handlers.
@return: A list of handlers to be installed in the opener.
@rtype: [Handler,...]
"""
handlers = []
unverified_context = ssl.create_default_context()
unverified_context.check_hostname = False
unverified_context.verify_mode = ssl.CERT_NONE
unverified_handler = urllib2.HTTPSHandler(context=unverified_context)
handlers.append(unverified_handler)
handlers.append(urllib2.ProxyHandler(self.proxy))
#handlers.append(urllib2.ProxyHandler(self.proxy))
return handlers
Nota: No es una forma recomendada de hacerlo.
Otra solución consiste en utilizar las solicitudes de biblioteca como transporte, que tiene una mejor compatibilidad con ssl. Esto es lo que estoy usando ahora para acceder a los servicios SOAP a través de https usando espuma: -
import requests
from suds.transport.http import HttpAuthenticated
from suds.transport import Reply, TransportError
class RequestsTransport(HttpAuthenticated):
def __init__(self, **kwargs):
self.cert = kwargs.pop(''cert'', None)
# super won''t work because not using new style class
HttpAuthenticated.__init__(self, **kwargs)
def send(self, request):
self.addcredentials(request)
resp = requests.post(request.url, data=request.message,
headers=request.headers, cert=self.cert)
result = Reply(resp.status_code, resp.headers, resp.content)
return result
Y luego puedes instanciar el cliente de suds como:
headers = {"Content-TYpe" : "text/xml;charset=UTF-8",
"SOAPAction" : ""}
t = RequestsTransport(cert=''/path/to/cert'', **credentials)
client = Client(wsdl_uri, location=send_url, headers=headers,
transport=t))
Actualizar
Ahora estamos usando Zeep , que usa requests
debajo.
Parece que desea autenticarse utilizando un certificado de cliente , no un certificado de servidor , como se indicó en algunos de los comentarios. Tuve el mismo problema y pude escribir un transporte personalizado para SUDS. Aquí está el código que funciona para mí.
Necesitará sus certificados en formato PEM para que esto funcione; OpenSSL puede realizar fácilmente esta conversión, aunque no recuerdo la sintaxis exacta.
import urllib2, httplib, socket
from suds.client import Client
from suds.transport.http import HttpTransport, Reply, TransportError
class HTTPSClientAuthHandler(urllib2.HTTPSHandler):
def __init__(self, key, cert):
urllib2.HTTPSHandler.__init__(self)
self.key = key
self.cert = cert
def https_open(self, req):
#Rather than pass in a reference to a connection class, we pass in
# a reference to a function which, for all intents and purposes,
# will behave as a constructor
return self.do_open(self.getConnection, req)
def getConnection(self, host, timeout=300):
return httplib.HTTPSConnection(host,
key_file=self.key,
cert_file=self.cert)
class HTTPSClientCertTransport(HttpTransport):
def __init__(self, key, cert, *args, **kwargs):
HttpTransport.__init__(self, *args, **kwargs)
self.key = key
self.cert = cert
def u2open(self, u2request):
"""
Open a connection.
@param u2request: A urllib2 request.
@type u2request: urllib2.Requet.
@return: The opened file-like urllib2 object.
@rtype: fp
"""
tm = self.options.timeout
url = urllib2.build_opener(HTTPSClientAuthHandler(self.key, self.cert))
if self.u2ver() < 2.6:
socket.setdefaulttimeout(tm)
return url.open(u2request)
else:
return url.open(u2request, timeout=tm)
# These lines enable debug logging; remove them once everything works.
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger(''suds.client'').setLevel(logging.DEBUG)
logging.getLogger(''suds.transport'').setLevel(logging.DEBUG)
c = Client(''https://YOUR_URL_HERE'',
transport = HTTPSClientCertTransport(''YOUR_KEY_AND_CERT.pem'',
''YOUR_KEY_AND_CERT.pem''))
print c