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.
399 lines
16 KiB
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()
|