Viewing file: Handler.py (22.4 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
######################################################################## # $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Http/Soap/Handler.py,v 1.9 2005/04/14 03:21:04 cogbuji Exp $ """ Exposes the SCore interface over SOAP
Copyright 2005 Fourthought, Inc. (USA). Detailed license and copyright information: http://4suite.org/COPYRIGHT Project home, documentation, distributions: http://4suite.org/ """
__all__ = ['FTSS_SOAP_NS', 'SOAP_NS', 'SOAP_ENCODING_STYLE', 'XMLSCHEMA_INSTANCE_NS', 'VersaQueryHandler', 'SoapHandler', 'GenericResourceMethod', 'RawFileSoapImp', 'FetchResource', 'CreateResource', 'DeleteResource', ]
import string, base64, sys, cStringIO, traceback, sha, time from xml.dom import Node
from Ft.Rdf._4versa import PrettyPrintResults, BASE_NS_MAP from Ft.Rdf.Parsers.Versa import VERSA_NS from Ft.Server import FTSERVER_NAMESPACE from Ft.Server.Common import Util, ResourceTypes, Schema from Ft.Server.Common.AclConstants import ANONYMOUS_USER_NAME from Ft.Server.Server import FtServerServerException from Ft.Server.Server import Error as ServerError from Ft.Server.Server.Http import BaseRequestHandler, Status from Ft.Server.Server.Http import FtServerHttpException, Error from Ft.Server.Server.SCore import GetRepository from Ft.Xml import Domlette, XPath, EMPTY_NAMESPACE
FTSS_SOAP_NS = '%s#services' % FTSERVER_NAMESPACE
XMLSCHEMA_INSTANCE_NS = 'http://www.w3.org/2001/XMLSchema-instance' SOAP_NS = "http://schemas.xmlsoap.org/soap/envelope/" SOAP_ENCODING_STYLE = "http://schemas.xmlsoap.org/soap/encoding/"
SESSION_HEADER_ID = "sessionId" SESSION_HEADER_KEY = "sessionKey" USER_HEADER_KEY = "authenticatingUser" PASSWORD_HEADER_KEY = "authenticatingPassword"
def VersaQueryHandler(repo,reqel,respbody,logFile): """Handler for Versa Queries"""
args = {} for child in [child for child in reqel.childNodes if child.nodeName != '#text']: name = child.localName try: value = child.firstChild.data except: value = None
args[str(name)] = value
query = args.get('query') scope = args.get('scope') if scope: scope = scope.strip()
encodeContent = False if args.has_key('base64') and args['base64'].lower() in ['true','1']: encodeContent = True
if args.has_key('base64'): del args['base64']
if not query: raise FtServerHttpException(Error.BAD_SOAP_QUERY)
from Ft.Rdf.Parsers import Versa from Ft.Rdf.Parsers.Versa.Util import ResultsToXml
if encodeContent: exp = Versa.Compile(base64.decodestring(query)) else: exp = Versa.Compile(query)
try: nsMappings = Util.GetUserNsMappings(repo, BASE_NS_MAP) nsMappings = ParseNsMappings(repo.fetchResource('/ftss/dashboard/NsMappings.xml').getContent(), {}) except: pass
con = Versa.CreateContext(model=repo.getModel(), nsMapping=nsMappings, scope=scope)
st = cStringIO.StringIO() ResultsToXml(exp.evaluate(con),st) resultRoot = "<Result xmlns='http://rdfinference.org/versa/0/2/'>%s</Result>" doc = Domlette.NonvalidatingReader.parseString(resultRoot%(st.getvalue()), uri='urn:uuid:versaResult') respbody.appendChild(doc.documentElement)
class SoapHandler(BaseRequestHandler.BaseRequestHandler): def __init__(self, connection): BaseRequestHandler.BaseRequestHandler.__init__(self, connection) config = vars(self.server) self.http_post_encoding_var = config.get('http_post_encoding_var', 'http-post-encoding') return
#This dictionary is to be used by overridden implementations to look up the correct actions. #The default action is to map the URI to a SOAP Server instance. #Then call the correct action on the instance. Since 90% of all of the parameters are strings, #All will be type cast to strings by the "default" handler. #The biggest exception is self will be the actual resource that was requested. NS_TO_HANDLER_MAPPING = { VERSA_NS : {'versa-query' : VersaQueryHandler,} }
def SOAPAuthenticate(self,SOAPdoc): self.sessionId = None soap_envelope = SOAPdoc.documentElement soap_header = filter(lambda x: (x.namespaceURI, x.localName) == (SOAP_NS, "Header"), soap_envelope.childNodes) if not soap_header: return
soap_header = soap_header[0] soap_authEl = filter(lambda x: (x.namespaceURI, x.localName) == (FTSS_SOAP_NS, "authenticationHeader"), soap_header.childNodes) if not soap_authEl: return soap_authEl = soap_authEl[0]
self.user = soap_authEl.xpath('string(*[local-name() = "%s" ])'%(USER_HEADER_KEY)) or None self.password = sha.new(soap_authEl.xpath('string(*[local-name() = "%s" ])'%(PASSWORD_HEADER_KEY)) or '').hexdigest() self.sessionId = soap_authEl.xpath('string(*[local-name() = "%s" ])'%(SESSION_HEADER_ID)) or None self.sessionKey = soap_authEl.xpath('string(*[local-name() = "%s" ])'%(SESSION_HEADER_KEY)) or None
def getRepository(self, sendError=1): """ Overidden to account for session handshaking """ if self.sessionId: from Ft.Server.Server.SCore import RetrieveSession repo = RetrieveSession(self.sessionId, self.sessionKey, self.server.errorLog, self.server.properties)
elif not self.user or self.user == ANONYMOUS_USER_NAME: # Anonymous login or no authentication required self.server.errorLog.debug("Repository access obtained for anonymous user") repo = GetRepository(None, None, self.server.errorLog, self.server.properties) else: try: repo = GetRepository(self.user, self.password, self.server.errorLog, self.server.properties) self.server.errorLog.debug("Repository access obtained for user %s" % self.user) except FtServerServerException, e: if sendError and e.errorCode in [ServerError.INVALID_LOGIN,]: # Generate an HTTP_FORBIDDEN response self.server.errorLog.error("Repository access denied for user %s; will send Forbidden response" % self.user) self.status = Status.HTTP_FORBIDDEN return None raise return repo
def do_POST(self): """ Handle a POST request that is a SOAP message """
#Look for the SOAP Action for name in self.headers_in.keys(): if name.lower() == 'soapaction': break else: raise FtServerHttpException(Error.MISSING_SOAPACTION_HEADER)
self.server.errorLog.info("Beginning processing of SOAP message (%s bytes)" % (len(self.body)))
#Read in the request document start = time.time() try: doc = Domlette.NonvalidatingReader.parseString(self.body, uri='http://schemas.xmlsoap.org/soap/envelope/') except Exception, e: print "Unparseable SOAP message: ", self.body raise FtServerHttpException(Error.MALFORMED_SOAP_MESSAGE, errmsg=str(e)) end = time.time() self.server.errorLog.info("Successfully parsed SOAP message in %0.3fs"%(end - start))
#Look four our handler function #this will raise an exception if not handled handlerfunc,reqbody = self._checkHeader(doc)
#Get a connection to the repository #check if user/passwd was supplied in SOAP header self.server.errorLog.info("Processing authentication header ..") self.SOAPAuthenticate(doc) repo = self.getRepository(sendError = 1) if repo is None: #An error was sent, just return return
commit = 0 try: #Fall into the main handling try: respbody = self._prepResponse(reqbody) commit = handlerfunc(repo, reqbody, respbody,self.server.errorLog) except: self.server.errorLog.info("Exception executing SOAP handler function") import traceback; traceback.print_exc() commit = 0 #Send a SOAP fault respbody = self._generateFault(reqbody) return self._finalizeResponse(reqbody, respbody)
finally: if commit: repo.txCommit() else: try: repo.txRollback() except: pass
def _checkHeader(self, doc): #Read in the SOAP request envelope #Make sure it is a valid request for us to handle
soap_envelope = doc.documentElement if (soap_envelope.namespaceURI, soap_envelope.localName) != (SOAP_NS, "Envelope"): #FIXME: Use HTTP SoapAction header for screening insead. #If it's present but the above condition is false, we send a client fault #FIXME: This should return a SOAP client fault raise FtServerHttpException(Error.INVALID_SOAP_ENVELOPE)
soap_body = filter(lambda x: (x.namespaceURI, x.localName) == (SOAP_NS, "Body"), soap_envelope.childNodes)[0] request_el = filter(lambda x: x.nodeType == Node.ELEMENT_NODE, soap_body.childNodes)[0] request_ns = request_el.namespaceURI request_name = request_el.localName
if not self.body: raise FtServerHttpException(Error.MISSING_SOAP_BODY)
handlerfunc = None #Determine our handler if request_ns in self.NS_TO_HANDLER_MAPPING: handlerfunc1 = self.NS_TO_HANDLER_MAPPING[request_ns] if request_name in handlerfunc1: handlerfunc = handlerfunc1[request_name] else: raise FtServerHttpException(Error.NO_SOAP_HANDLER_FOR_NAME, name=request_name, ns=request_ns) elif request_ns == FTSS_SOAP_NS: handlerfunc = GenericResourceMethod else: #Otherwise no clue!!! raise FtServerHttpException(Error.NO_SOAP_HANDLER_FOR_NS, ns=request_ns)
return handlerfunc, request_el
def _prepResponse(self, doc): #Create the response document try: dt = Domlette.implementation.createDocumentType("SOAP-ENV:Envelope", '', '') except: dt = None respdoc = Domlette.implementation.createDocument(SOAP_NS, "SOAP-ENV:Envelope",dt) respdocel = respdoc.documentElement respdocel.setAttributeNS(SOAP_NS, "SOAP-ENV:encodingStyle", SOAP_ENCODING_STYLE) respbody = respdoc.createElementNS(SOAP_NS, "SOAP-ENV:Body") respdocel.appendChild(respbody) return respbody
def _finalizeResponse(self, reqbody, respbody): #Serialize the results self.status = Status.HTTP_OK Domlette.PrettyPrint(respbody.ownerDocument, stream=self.wfile) self.headers_out['Content-Type'] = 'text/xml' return
def _generateFault(self,reqbody):
#Create a new respbody respbody = self._prepResponse(reqbody)
eType, ex = sys.exc_info()[:2]
doc = respbody.ownerDocument
if 0: #FIXME Capture specific exceptions pass else: #Unknown Fault faultCode = "SOAP-ENV:Server" #faultCode = "" faultString = str(ex) faultFactor = None st = cStringIO.StringIO() traceback.print_exc(file=st) detailStr = base64.encodestring(st.getvalue()) detail = doc.createElementNS(EMPTY_NAMESPACE,'detail') tb = doc.createElementNS(FTSS_SOAP_NS,'ftsoap:traceback') tb.appendChild(doc.createTextNode(detailStr)) detail.appendChild(tb)
fault = doc.createElementNS(SOAP_NS,'SOAP-ENV:Fault') fc = doc.createElementNS(EMPTY_NAMESPACE,'faultcode') fc.appendChild(doc.createTextNode(str(faultCode))) fault.appendChild(fc)
fs = doc.createElementNS(EMPTY_NAMESPACE,'faultstring') fs.appendChild(doc.createTextNode(str(faultString))) fault.appendChild(fs)
if faultFactor: ff = doc.createElementNS(EMPTY_NAMESPACE,'faultfactor') ff.appendChild(doc.createTextNode(str(faultFactor))) fault.appendChild(ff)
if detail: fault.appendChild(detail)
respbody.appendChild(fault) return respbody
def get_form_encoding(self, form): if self.form_encoding: return import cgi encoding = 'ISO-8859-1' fields = cgi.parse_qs(form) if fields.has_key(self.http_post_encoding_var): encoding = fields[self.http_post_encoding_var][0] self.form_encoding = encoding
def GenericResourceMethod(repo,reqel,respbody,logFile):
#If, we have fallen to this then we want to perform a generic request on the #On a resource. Use the path attribute to get the resource path = reqel.xpath('string(./*[local-name() = "srcpath"])')
if not path: raise ValueError("Missing (or illegally specified) required 'srcpath' parameter")
res = repo.fetchResource(path)
#Now, mapp the resource type to the local implementation #l = g_resourceTypeMapping[res.resourceType](res) l = RawFileSoapImp(res) return l.execute(reqel,respbody,logFile)
class RawFileSoapImp:
integerArguments = [ 'createDocument.forcedType', 'createContainer.createParents' ]
def __init__(self,res): self.resource = res
def execute(self,reqel,respbody,logFile): """ See if we define the requested action, ifso call that, otherwise, just call it on the resource """ mname = reqel.localName[0].lower() + reqel.localName[1:] #print mname if hasattr(self,mname): #Call it special return getattr(self,mname)(res,reqel,respbody)
#If not, just call them all assuming that ever attribute mapps to a string args = {}
for child in [child for child in reqel.childNodes if child.nodeName != '#text']: name = child.localName try: value = child.firstChild.data except: value = None
if name == 'srcpath': srcpath = value continue
if string.join([mname,str(name)],'.') in self.integerArguments: args[str(name)] = int(value) else: args[str(name)] = value
#Special case, if method is fetchResource, then assume src-path referes to the resource we wish #to retrieve. Also check for base64 attribute which determines whether or not to encode the returned #content (encodes the content by default) encodeContent = True if mname == 'fetchResource': args['path'] = srcpath
if args.has_key('base64') and args['base64'].lower() in ['false','0']: encodeContent = False
if args.has_key('base64'): del args['base64']
#See if there is a src or updateSrc argument. If so check for a 'base64' attribute (assume encoded if) #doesn't exist if encodeContent: if args.has_key('src'): start = time.time() args['src'] = base64.decodestring(args['src']) end = time.time() logFile.info("Time to decode Base64 'src' argument: %s seconds" % (end - start))
if args.has_key('updateSrc'): start = time.time() args['updateSrc'] = base64.decodestring(args['updateSrc']) end = time.time() logFile.info("Time to decode Base64 'updateSrc' argument: %s seconds" % (end - start))
meth = getattr(self.resource, mname) msg = "%s(" % mname for name, value in args.items(): if name in ['src','updateSrc']: value = "..." msg += "%s='%s'," % (name, value) msg += ')' logFile.info("Executing SOAP command: %s on %s" % (msg, self.resource.getAbsolutePath()))
start = time.time() if mname == 'xUpdate': print "Special command (xUpdate). Processed explicitely" from Ft.Xml.Domlette import Print, NonvalidatingReader from Ft.Xml import XUpdate from Ft.Rdf.Triclops import ParseNsMappings resDom = self.resource.asDom() userMappings = Util.GetUserNsMappings(repo, BASE_NS_MAP) XUpdate.Processor().execute(resDom, NonvalidatingReader.parseString(args['updateSrc'], uri="urn:uuid:XUpdate"), processorNss=userMappings) st = cStringIO.StringIO() Print(resDom,stream=st) res = self.resource.setContent(st.getvalue()) else: res = meth(**args) end = time.time()
logFile.info("Time to execute command: %s seconds" % (end - start)) logFile.info("result of command: %s"%(res))
if mname == 'applyXslt': #FIXME Other special cases: applyXslt (return results only) res = res[0]
if hasattr(res, 'resourceType'): self._makeResourceResponse(res,respbody,encodeContent) elif res is not None: self._makeValueResponse(res,respbody) else: self._makeSuccessResponse(respbody) return 1
def _makeSuccessResponse(self, respbody): doc = respbody.ownerDocument succRespNode = doc.createElementNS(FTSS_SOAP_NS,'ftsoap:successResponse') respbody.appendChild(succRespNode)
def _makeValueResponse(self, res,respbody): doc = respbody.ownerDocument valRespNode = doc.createElementNS(FTSS_SOAP_NS,'ftsoap:valueResponse') valRespNode.appendChild(doc.createTextNode(str(res))) respbody.appendChild(valRespNode)
def _makeResourceResponse(self, res,respbody,encodeContent): doc = respbody.ownerDocument cont = res.getContent() imt = res.getImt() resNode = doc.createElementNS(FTSERVER_NAMESPACE,'ftss:Resource') md=res.getMetaDataResource().getContent() mdDom = Domlette.NonvalidatingReader.parseString(md,uri=res.getAbsolutePath()) resNode.appendChild(mdDom.documentElement)
if res.resourceType == ResourceTypes.ResourceType.CONTAINER: #print self.resource.values() containerNode = doc.createElementNS(FTSERVER_NAMESPACE,'ftss:Children') for index in xrange(len(self.resource)): try: child = self.resource[index] childRefNode = doc.createElementNS(FTSERVER_NAMESPACE,'ftss:ChildReference') childMD=child.getMetaDataResource().getContent() Domlette.NonvalidatingReader.parseString(childMD,uri=child.getAbsolutePath()) childRefNode.appendChild( Domlette.NonvalidatingReader.parseString(childMD,uri=child.getAbsolutePath()).documentElement ) containerNode.appendChild(childRefNode) except: raise resNode.appendChild(containerNode) else: contentNode = doc.createElementNS(FTSERVER_NAMESPACE,'ftss:Data') if encodeContent: contentNode.appendChild(doc.createTextNode(base64.encodestring(cont))) contentNode.setAttributeNS(XMLSCHEMA_INSTANCE_NS,'xsi:type','xsd:base64Binary') else: contentNode.appendChild(doc.createTextNode(cont)) contentNode.setAttributeNS(XMLSCHEMA_INSTANCE_NS,'xsi:type','xsd:string') resNode.appendChild(contentNode) respbody.appendChild(resNode)
resourceNameMapping = {ResourceTypes.ResourceType.RAW_FILE:'RawFile', ResourceTypes.ResourceType.XML_DOCUMENT:'XmlDocument', ResourceTypes.ResourceType.CONTAINER:'Container', }
g_resourceTypeMapping = {ResourceTypes.ResourceType.RAW_FILE:RawFileSoapImp, ResourceTypes.ResourceType.XML_DOCUMENT:RawFileSoapImp, ResourceTypes.ResourceType.CONTAINER:RawFileSoapImp, }
def FetchResource(repo, reqel, respbody): doc = respbody.ownerDocument context = XPath.Context.Context(reqel) #context = XPath.Context.Context(reqel, processorNss={'ft': XMLSERVER_NS}) uri = XPath.Evaluate('string(@uri)', context=context) try: res = repo.fetchResource(uri) except: GenerateFault(respbody) return 0 _MakeResourceResponse(res,respbody) return 0
def CreateResource(repo, reqel, respbody): doc = respbody.ownerDocument context = XPath.Context.Context(reqel, processorNss={'ft': FTSS_SOAP_NS}) cont = XPath.Evaluate('ft:Container', context=context) ddName = XPath.Evaluate('string(@doc-def)', context=context)
if cont: context = XPath.Context.Context(cont[0], processorNss={'ft': FTSS_SOAP_NS}) uri = XPath.Evaluate('string(@uri)', context=context) cp = XPath.Evaluate('number(@create-parents)', context=context) try: res = repo.createContainer(uri, createParents=cp) except: GenerateFault(respbody) return 0 elif ddName: uri = XPath.Evaluate('string(@uri)', context=context) source = base64.decodestring(XPath.Evaluate('string(node())', context=context)) res = repo.createDocument(ddName, uri, source) else: GenerateFault(respbody) return 0 _MakeResourceResponse(res,respbody) return 1
def DeleteResource(repo, reqel, respbody): doc = respbody.ownerDocument context = XPath.Context.Context(reqel) #context = XPath.Context.Context(reqel, processorNss={'ft': XMLSERVER_NS}) uri = XPath.Evaluate('string(@uri)', context=context) try: repo.deleteResource(uri) except: GenerateFault(respbody) return 0
return 1
|