You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-core/pym/wsdl_server.py

399 lines
16 KiB

#-*- coding: utf-8 -*-
# Copyright 2010 Calculate Ltd. http://www.calculate-linux.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import inspect
import logging
import re
import sys
import traceback
from SOAPpy import SOAPServer
from SOAPpy import SOAPConfig
from SOAPpy import SOAPRequestHandler
#SOAPConfig.debug =1
import xml.dom.minidom
import xml.dom.ext
import cStringIO
from M2Crypto import SSL
class WSDLRequestHandler(SOAPRequestHandler):
def do_POST(self):
data = self.rfile.read(int(self.headers["Content-length"]))
doc = xml.dom.minidom.parseString(data)
for node in doc.documentElement.firstChild.childNodes:
if node.nodeType == node.ELEMENT_NODE:
if node.hasAttribute("xsi:nil"):
node.removeAttribute("xsi:nil")
xmlData = doc.toxml(encoding='UTF-8')
lenXml = len(xmlData)
self.headers["Content-length"] = str(lenXml)
self.rfile = cStringIO.StringIO(xmlData)
return SOAPRequestHandler.do_POST(self)
def do_GET(self):
"""Return the WSDL.
This is a naive hobbled implementation it only deals with the
first namespace in the server. That's because I don't know how
WSDL deals with multiple namespaces."""
def getTypesArg(text, funcName=""):
resSearch = \
re.search(\
"##wsdl:(([^: \t]+:[^: \t,]+)(\s*,\s*[^: \t]+:[^: \t,]+)+)",
" ".join(filter(lambda x: x, re.compile("[\t ]").split(\
text.replace("\n"," ")))))
if resSearch:
return re.compile("\s?,\s?").split(resSearch.groups(0)[0])
else:
if funcName:
raise Exception("Wrong ##wdsl options in function '%s'"\
%funcName)
else:
raise Exception("Wrong ##wdsl options in function")
logger = logging.getLogger("do_GET")
WSDL = "http://schemas.xmlsoap.org/wsdl/"
# Create the doc with the necessary namespaces
wsdlDoc = xml.dom.minidom.parseString("""
<wsdl:definitions
name="auto_generated"
targetNamespace="API"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="API"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"/>""")
# docend
try:
self.server.funcmap[self.server.funcmap.keys()[0]]
for servCalls in map(lambda x: self.server.funcmap[x],
self.server.funcmap.keys()):
# Introspect the current service
dataList = []
# Make the messages from each function
for callName, call in servCalls.iteritems():
if callName=="_auth":
continue
func = call.func
if hasattr(func,"argv_list"):
funcArg = getattr(func,"argv_list")
else:
funcArg = inspect.getargspec(func)[0]
if funcArg and funcArg[0] == 'self':
funcArg = funcArg[1:]
# Get the types from the programmer provided type spec
funcName = func.__name__
typesFunc = getTypesArg(func.__doc__, funcName)
argTypes = typesFunc[0:-1]
returnType = typesFunc[-1:][0]
assert len(funcArg) == len(argTypes)
dataList.append((callName, funcName, funcArg, argTypes,
returnType))
# Types
typesNode =\
wsdlDoc.firstChild.appendChild(wsdlDoc.createElement(
"wsdl:types"))
schemaNode = typesNode.appendChild(wsdlDoc.createElement("schema"))
schemaNode.setAttribute("targetNamespace", "API")
schemaNode.setAttribute("xmlns", "http://www.w3.org/2001/XMLSchema")
schemaNode.appendChild(
wsdlDoc.createElement("import")).setAttribute("namespace",
"http://schemas.xmlsoap.org/soap/encoding/")
# Add Type
attrNode = wsdlDoc.createElement("attribute")
attrNode.setAttribute("ref", "soapenc:arrayType")
attrNode.setAttribute("wsdl:arrayType","xsd:string[]")
restNode = wsdlDoc.createElement("restriction")
restNode.setAttribute("base","soapenc:Array")
restNode.appendChild(attrNode)
contNode = wsdlDoc.createElement("complexContent")
contNode.appendChild(restNode)
typeNode = wsdlDoc.createElement("complexType")
typeNode.setAttribute("name","ArrayOfString")
typeNode.appendChild(contNode)
schemaNode.appendChild(typeNode)
for callName, funcName, funcArg, argTypes, returnType in dataList:
# Now add the in parameters
inMsgNode =\
wsdlDoc.firstChild.appendChild(wsdlDoc.createElement(
"wsdl:message"))
inMsgNode.setAttribute("name", funcName + "_in")
for i in range(0, len(funcArg)):
arg = funcArg[i]
arg_part =\
inMsgNode.appendChild(wsdlDoc.createElement(
"wsdl:part"))
arg_part.setAttribute("name", arg)
arg_part.setAttribute("type", argTypes[i])
# Set the return parameter
outMsgNode =\
wsdlDoc.firstChild.appendChild(wsdlDoc.createElement(
"wsdl:message"))
outMsgNode.setAttribute("name", funcName + "_out")
ret_part = outMsgNode.appendChild(wsdlDoc.createElement(
"wsdl:part"))
ret_part.setAttribute("name", "returnvalue")
ret_part.setAttribute("type", returnType)
# Now the port connecting the two together
portNode =\
wsdlDoc.firstChild.appendChild(wsdlDoc.createElement(
"wsdl:portType"))
portNode.setAttribute("name", "APIPort")
for callName, funcName, funcArg, argTypes, returnType in dataList:
opNode =\
portNode.appendChild(wsdlDoc.createElement(
"wsdl:operation"))
opNode.setAttribute("name", callName)
opNode.appendChild(
wsdlDoc.createElement("wsdl:input")).setAttribute("message",
"tns:" + callName + "_in")
opNode.appendChild(wsdlDoc.createElement(
"wsdl:output")).setAttribute("message",
"tns:" + callName + "_out")
# Now the binding
bindNode =\
wsdlDoc.firstChild.appendChild(wsdlDoc.createElement(
"wsdl:binding"))
bindNode.setAttribute("name", "APIBinding")
bindNode.setAttribute("type", "tns:" + "APIPort")
soapBindNode =\
bindNode.appendChild(wsdlDoc.createElement("soap:binding"))
soapBindNode.setAttribute("style", "rpc")
soapBindNode.setAttribute("transport",
"http://schemas.xmlsoap.org/soap/http")
for callName, funcName, funcArg, argTypes, returnType in dataList:
wsdlOpNode =\
bindNode.appendChild(wsdlDoc.createElement(
"wsdl:operation"))
wsdlOpNode.setAttribute("name", callName)
wsdlOpNode.appendChild(wsdlDoc.createElement(
"soap:operation")).setAttribute(
"soapAction",
"API#" + callName)
wsdlInNode =\
wsdlOpNode.appendChild(wsdlDoc.createElement("wsdl:input"))
wsdlInNode.setAttribute("name", callName + "_in")
soapBodyInNode =\
wsdlInNode.appendChild(wsdlDoc.createElement("soap:body"))
soapBodyInNode.setAttribute("use", "encoded")
soapBodyInNode.setAttribute("namespace", "API")
soapBodyInNode.setAttribute("encodingStyle",
"http://schemas.xmlsoap.org/soap/encoding/")
wsdlOutNode =\
wsdlOpNode.appendChild(wsdlDoc.createElement("wsdl:output"))
wsdlOutNode.setAttribute("name", callName + "_out")
soapBodyOutNode =\
wsdlOutNode.appendChild(wsdlDoc.createElement("soap:body"))
soapBodyOutNode.setAttribute("use", "encoded")
soapBodyOutNode.setAttribute("namespace", "API")
soapBodyOutNode.setAttribute("encodingStyle",
"http://schemas.xmlsoap.org/soap/encoding/")
# Now the service
serviceNode =\
wsdlDoc.firstChild.appendChild(wsdlDoc.createElement(
"wsdl:service"))
serviceNode.setAttribute("name", "CalculateAPIService")
servPortNode =\
serviceNode.appendChild(wsdlDoc.createElement("wsdl:port"))
servPortNode.setAttribute("name", "APIPort")
servPortNode.setAttribute("binding", "tns:" + "APIBinding")
soapAddressNode =\
servPortNode.appendChild(wsdlDoc.createElement("soap:address"))
soapAddressNode.setAttribute("location", "https://%s:%d"\
%self.server.server_address)
except Exception, e:
logger.error(e)
print traceback.format_tb(sys.exc_info()[2])
self.send_response(500)
self.send_header("content-type", "text/html")
self.end_headers()
self.wfile.write("""
<html>
<body>
<h1>There was an error generating the WSDL</h1>
<ul>
<li>%s</li>
%s
</ul>
</body>
</html>""" % (str(e), "".join(["<li>" + l + "</li>" for l in
traceback.format_tb(sys.exc_info()[2])])))
else:
# Send it.
self.send_response(200)
self.send_header("content-type", "application/xml")
self.end_headers()
xml.dom.ext.PrettyPrint(wsdlDoc, stream = self.wfile)
return None
_REQUEST_AUTH_DATA = ""
class clSOAPServer(SOAPServer):
def get_request(self):
sock, addr = SOAPServer.get_request(self)
if self.ssl_context:
subjectDict = {}
subjectDict.update(map(lambda x: x.split("="),
str(sock.get_peer_cert().get_subject()).split("/")[1:]))
globals()['_REQUEST_AUTH_DATA'] = subjectDict["OU"]
return sock, addr
def _encode(data, encoding="UTF-8"):
result = data
if type(data) is unicode:
result = data.encode(encoding)
elif type(data) is tuple:
result = tuple(map(lambda x: _encode(x, encoding), data))
elif type(data) is list:
result = map(lambda x: _encode(x, encoding), data)
elif type(data) is dict:
result = {}
result.update(map(lambda x:\
(_encode(x[0], encoding),
_encode(x[1],encoding)),
data.items()))
return result
def _decode(data, encoding="UTF-8"):
result = data
if type(data) is str:
result = data.decode(encoding)
elif type(data) is tuple:
result = tuple(map(lambda x: _decode(x, encoding), data))
elif type(data) is list:
result = map(lambda x: _decode(x, encoding), data)
elif type(data) is dict:
result = {}
result.update(map(lambda x:\
(_decode(x[0], encoding),
_decode(x[1],encoding)),
data.items()))
return result
def _unicodeWrapper(method):
def wrapper(*args, **kwargs):
args = _encode(args)
kwargs = _encode(kwargs)
ret = _decode(method.__call__(*args, **kwargs))
return ret
return wrapper
class unicodeWrapper:
def _createFunc(self, method):
@_unicodeWrapper
def function(*args, **kwargs):
return method(*args, **kwargs)
return function
def __init__(self, obj):
listAttrObj = map(lambda x: (x,getattr(obj, x)),
filter(lambda x: not x.startswith('__'), dir(obj)))
for name, attr in listAttrObj:
attr.__doc__
if callable(attr) and attr.__doc__ and "##wsdl:" in attr.__doc__:
funcArg = inspect.getargspec(attr)[0]
if funcArg and funcArg[0] == 'self':
funcArg = funcArg[1:]
func = self._createFunc(attr)
setattr(func, "__doc__", attr.__doc__)
setattr(func, "__name__", attr.__name__)
setattr(func, "argv_list", funcArg)
setattr(self, name, func)
_DICT_METHODS = {}
class auth:
def _auth(self, *arg, **kwarg):
if '_SOAPContext' in kwarg:
_SOAPContext = kwarg['_SOAPContext']
if _SOAPContext:
methodName = _SOAPContext.body._keys()[0]
if not methodName in _DICT_METHODS.keys():
return False
obj = _DICT_METHODS[methodName]
return obj.auth(_REQUEST_AUTH_DATA)
return True
_LIST_OBJ = []
def registerApiObject(apiObj):
_LIST_OBJ.append((apiObj,unicodeWrapper(apiObj)))
_CA_FILE = ""
_CERT_FILE = ""
_KEY_FILE = ""
def setCertificates(cafile, certfile, keyfile):
globals()['_CA_FILE'] = cafile
globals()['_CERT_FILE'] = certfile
globals()['_KEY_FILE'] = keyfile
_ADDR = ()
_DEBUG = False
_LOG = 0
def setServerData(host="localhost", port=8080, debug=_DEBUG, log=_LOG):
globals()['_ADDR'] = (host, [port][0])
globals()['_DEBUG'] = debug
globals()['_LOG'] = log
def do_soap():
ssl_context = SSL.Context('sslv3')
ssl_context.load_verify_locations(cafile=_CA_FILE)
ssl_context.load_cert(certfile=_CERT_FILE, keyfile=_KEY_FILE)
ssl_context.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert,
depth=9)
server = clSOAPServer(RequestHandler = WSDLRequestHandler,
addr = _ADDR,
ssl_context = ssl_context,
config=SOAPConfig(authMethod="_auth", debug=_DEBUG), log=_LOG)
authObi = auth()
server.registerKWFunction(authObi._auth, "API")
for obj, wrapObj in _LIST_OBJ:
for nameMethod in filter(lambda x: not x.startswith("_"), dir(wrapObj)):
server.registerKWFunction(getattr(wrapObj, nameMethod), "API")
if nameMethod in _DICT_METHODS.keys():
dictErrMess = {"serv1":_DICT_METHOD[nameMethod].service,
"serv2":obj.service,
"method":nameMethod}
raise Exception("Duplicate registered method %(method)s "
"in services %(serv1)s and %(serv2)s"\
%dictErrMess)
_DICT_METHODS[nameMethod] = obj
server.serve_forever()