import os, sys import cherrypy from mako.template import Template from mako.lookup import TemplateLookup import simplejson import datetime, decimal from StringIO import StringIO from invirt.config import structs as config import invirt.database from webcommon import State class MakoHandler(cherrypy.dispatch.LateParamPageHandler): """Callable which processes a dictionary, returning the rendered body.""" def __init__(self, template, next_handler, content_type='text/html; charset=utf-8'): self.template = template self.next_handler = next_handler self.content_type = content_type def __call__(self): env = globals().copy() env.update(self.next_handler()) cherrypy.response.headers['Content-Type'] = self.content_type return self.template.render(**env) class MakoLoader(object): def __init__(self): self.lookups = {} def get_lookup(self, directories, module_directory=None, collection_size=-1, imports=[], **kwargs): # Find the appropriate template lookup. key = (tuple(directories), module_directory) try: lookup = self.lookups[key] except KeyError: lookup = TemplateLookup(directories=directories, module_directory=module_directory, collection_size=collection_size, default_filters=['decode.utf8'], input_encoding='utf-8', output_encoding='utf-8', imports=imports, ) self.lookups[key] = lookup return lookup def __call__(self, filename, directories, module_directory=None, collection_size=-1, content_type='text/html; charset=utf-8', imports=[]): cherrypy.request.lookup = lookup = self.get_lookup( directories, module_directory, collection_size, imports) cherrypy.request.template = t = lookup.get_template(filename) cherrypy.request.handler = MakoHandler( t, cherrypy.request.handler, content_type) cherrypy.tools.mako = cherrypy.Tool('on_start_resource', MakoLoader()) def revertStandardError(): """Move stderr to stdout, and return the contents of the old stderr.""" errio = sys.stderr if not isinstance(errio, StringIO): return '' sys.stderr = sys.stdout errio.seek(0) return errio.read() def catchStderr(): old_handler = cherrypy.request.handler def wrapper(*args, **kwargs): sys.stderr = StringIO() ret = old_handler(*args, **kwargs) e = revertStandardError() if e: if isinstance(ret, dict): ret["error_text"] = e return ret if old_handler: cherrypy.request.handler = wrapper cherrypy.tools.catch_stderr = cherrypy.Tool('before_handler', catchStderr) class JSONEncoder(simplejson.JSONEncoder): def default(self, obj): if isinstance(obj, datetime.datetime): return str(obj) elif isinstance(obj, decimal.Decimal): return float(obj) else: return simplejson.JSONEncoder.default(self, obj) def jsonify_tool_callback(*args, **kwargs): if not cherrypy.request.cached: response = cherrypy.response response.headers['Content-Type'] = 'text/javascript' response.body = JSONEncoder().iterencode(response.body) cherrypy.tools.jsonify = cherrypy.Tool('before_finalize', jsonify_tool_callback, priority=30) def require_login(): """If the user isn't logged in, raise 403 with an error.""" if cherrypy.request.login is False: raise cherrypy.HTTPError(403, "You are not authorized to access that resource") cherrypy.tools.require_login = cherrypy.Tool('on_start_resource', require_login, priority=150) def require_POST(): """If the request isn't a POST request, raise 405 Method Not Allowed""" if cherrypy.request.method != "POST": raise cherrypy.HTTPError(405, "You must submit this request with POST") cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource', require_POST, priority=150) def remote_user_login(): """Get remote user from SSL or GSSAPI, and store in request object. Get the current user based on environment variables set by SSL or GSSAPI, and store it in the attribute cherrpy.request.login. Per the CherryPy API (http://www.cherrypy.org/wiki/RequestObject#login), the attribute is set to the username on successful login, to False on failed login, and is left at None if the user attempted no authentication. """ environ = cherrypy.request.wsgi_environ user = environ.get('REMOTE_USER') if user is None: return if environ.get('AUTH_TYPE') == 'Negotiate': # Convert the krb5 principal into a krb4 username if not user.endswith('@%s' % config.kerberos.realm): cherrypy.request.login = False # failed to log in else: cherrypy.request.login = user.split('@')[0].replace('/', '.') else: cherrypy.request.login = user cherrypy.tools.remote_user_login = cherrypy.Tool('on_start_resource', remote_user_login, priority=50) def invirtwebstate_init(): """Initialize the cherrypy.request.state object from Invirt""" if not hasattr(cherrypy.request, "state"): cherrypy.request.state = State(cherrypy.request.login) cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource', invirtwebstate_init, priority=100) cherrypy.tools.clear_db_cache = cherrypy.Tool('on_start_resource', invirt.database.clear_cache) class View(object): _cp_config = {'tools.mako.directories': [os.path.join(os.path.dirname(__file__),'templates')]}