from Globals import InitializeClass from OFS.SimpleItem import SimpleItem from Products.CMFCore.utils import UniqueObject, getToolByName from AccessControl import ClassSecurityInfo from AccessControl import getSecurityManager from AccessControl import Unauthorized from proxy import proxy from security import apply # this is a list of all the headers we would like to pass on from the server # to the ajax application, in case it uses them, its rare you need this, # but just in case... # its bad form to pass on all the headers blindly, some like # connection headers do not make sense headers_return = [ ("Accept-Language"), ] class Tool(UniqueObject, SimpleItem): """ Our tool for mangling requests up """ meta_type = "AjaxProxy" id = "ajax_proxy_tool" security = ClassSecurityInfo() # the heart of the tool, very simply anything starting with proxy # is grabbed and passed of to ajax_proxy_call, the rest is ignored security.declarePublic("__bobo_traverse__") def __bobo_traverse__(self, REQUEST, name): """ When something comes a traversing """ if name == "proxy": # stop all other traversing and proxy REQUEST.set('TraversalRequestNameStack', []) return self.ajax_proxy_call # ignore this, allows the ZMI to function # how could SimpleItem ever be None? if SimpleItem: return SimpleItem.__bobo_traverse__(self, REQUEST, name) security.declarePublic("ajax_proxy_call") def ajax_proxy_call(self, REQUEST=None, RESPONSE=None): """ Ensure this is publishable """ req, res = REQUEST, RESPONSE p = zopeproxy(self, req, res) # we need to chop off everything up to ...../proxy chop = len(self.absolute_url(1) + "/proxy") + 1 url = req['PATH_INFO'][chop:] headers = {} for k, v in req.items(): headers[k] = v # pass in the headers and url they asked for # past here the process has no idea this is zope in theory data, proxy_response = p.process(headers, url) for name in headers_return: try: value = proxy_response.getheader(name, None) except AttributeError: value = proxy_response.get(name, None) if value: res.setHeader(name, value) # with the response do whatever Zope needs to do here # now set some headers res.setHeader("Content-type", "text/xml") return data class zopeproxy(proxy): def __init__(self, ref, req, res): self.__ref = ref self.__req = req self.__res = res proxy.__init__(self) def get_request_method(self): """ Zope specific? """ return self.__req["REQUEST_METHOD"] def get_incoming_data(self): """ Again how we process the request to find the data is a bit of a Zope oddity """ return self.__req.form def headers_pass(self, headers): """ Zope specific code to grab the wierdo Zope headers and pass them on """ new_headers = {} new_headers["X-Proxied-For"] = headers["REMOTE_ADDR"] return new_headers def apply_security(self, headers, config): """ Applying security is a Zope thing, let's leave it in here """ # if there are no permissions set in the config file # we assume this is true permissions = config.get("security", None) if permissions is None: return permissions = permissions.split(',') permissions = [ p.strip() for p in permissions ] # not allowed by default allowed = False # loop through the permissions, when we find one that # works, we are good to go for permission in permissions: if getSecurityManager().checkPermission(permission, self.__ref): allowed = True return raise Unauthorized, "You do not have one of the permissions: %s to access that proxy" % str(permissions) InitializeClass(Tool)