source: trunk/packages/invirt-web/code/view.py @ 3030

Last change on this file since 3030 was 2993, checked in by broder, 15 years ago

Clear all objects from the SQLAlchemy session at the start of each request.

The SA identity mapper isn't *supposed* to be a cache, but it sure can
act like one sometimes.

File size: 6.0 KB
RevLine 
[2693]1import os, sys
[2659]2
3import cherrypy
4from mako.template import Template
5from mako.lookup import TemplateLookup
6import simplejson
7import datetime, decimal
[2693]8from StringIO import StringIO
[2669]9from invirt.config import structs as config
[2993]10import invirt.database
[2669]11from webcommon import State
[2659]12
[2723]13
[2659]14class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
[2721]15    """Callable which processes a dictionary, returning the rendered body."""
[2659]16   
[2724]17    def __init__(self, template, next_handler,
18                 content_type='text/html; charset=utf-8'):
[2659]19        self.template = template
20        self.next_handler = next_handler
21        self.content_type = content_type
22   
23    def __call__(self):
24        env = globals().copy()
25        env.update(self.next_handler())
26        cherrypy.response.headers['Content-Type'] = self.content_type
27        return self.template.render(**env)
28       
29
30class MakoLoader(object):
31   
32    def __init__(self):
33        self.lookups = {}
[2693]34
35    def get_lookup(self, directories, module_directory=None,
36                     collection_size=-1, imports=[], **kwargs):
[2659]37        # Find the appropriate template lookup.
38        key = (tuple(directories), module_directory)
39        try:
40            lookup = self.lookups[key]
41        except KeyError:
42            lookup = TemplateLookup(directories=directories,
43                                    module_directory=module_directory,
44                                    collection_size=collection_size,
45                                    default_filters=['decode.utf8'],
46                                    input_encoding='utf-8',
47                                    output_encoding='utf-8',
[2673]48                                    imports=imports,
[2659]49                                    )
50            self.lookups[key] = lookup
[2693]51        return lookup
52
53    def __call__(self, filename, directories, module_directory=None,
54                 collection_size=-1, content_type='text/html; charset=utf-8',
55                 imports=[]):
[2724]56        cherrypy.request.lookup = lookup = self.get_lookup(
57            directories, module_directory, collection_size, imports)
[2659]58        cherrypy.request.template = t = lookup.get_template(filename)
[2724]59        cherrypy.request.handler = MakoHandler(
60            t, cherrypy.request.handler, content_type)
[2659]61
[2699]62cherrypy.tools.mako = cherrypy.Tool('on_start_resource', MakoLoader())
[2659]63
[2723]64
[2693]65def revertStandardError():
66    """Move stderr to stdout, and return the contents of the old stderr."""
67    errio = sys.stderr
68    if not isinstance(errio, StringIO):
69        return ''
70    sys.stderr = sys.stdout
71    errio.seek(0)
72    return errio.read()
73
[2723]74
[2693]75def catchStderr():
76    old_handler = cherrypy.request.handler
77    def wrapper(*args, **kwargs):
78        sys.stderr = StringIO()
79        ret = old_handler(*args, **kwargs)
80        e = revertStandardError()
81        if e:
82            if isinstance(ret, dict):
83                ret["error_text"] = e
84        return ret
85    if old_handler:
86        cherrypy.request.handler = wrapper
87
88cherrypy.tools.catch_stderr = cherrypy.Tool('before_handler', catchStderr)
89
[2723]90
[2659]91class JSONEncoder(simplejson.JSONEncoder):
92        def default(self, obj):
93                if isinstance(obj, datetime.datetime):
94                        return str(obj)
95                elif isinstance(obj, decimal.Decimal):
96                        return float(obj)
97                else:
98                        return simplejson.JSONEncoder.default(self, obj)
99
[2723]100
[2659]101def jsonify_tool_callback(*args, **kwargs):
102    if not cherrypy.request.cached:
103        response = cherrypy.response
104        response.headers['Content-Type'] = 'text/javascript'
105        response.body = JSONEncoder().iterencode(response.body)
106
[2724]107cherrypy.tools.jsonify = cherrypy.Tool('before_finalize',
108                                       jsonify_tool_callback, priority=30)
[2659]109
[2723]110
[2665]111def require_login():
112    """If the user isn't logged in, raise 403 with an error."""
[2712]113    if cherrypy.request.login is False:
[2665]114        raise cherrypy.HTTPError(403,
115            "You are not authorized to access that resource")
116
[2724]117cherrypy.tools.require_login = cherrypy.Tool('on_start_resource',
118                                             require_login, priority=150)
[2665]119
[2723]120
[2685]121def require_POST():
122    """If the request isn't a POST request, raise 405 Method Not Allowed"""
123    if cherrypy.request.method != "POST":
124        raise cherrypy.HTTPError(405,
125                                 "You must submit this request with POST")
126
[2724]127cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource',
128                                            require_POST, priority=150)
[2685]129
[2723]130
[2670]131def remote_user_login():
[2722]132    """Get remote user from SSL or GSSAPI, and store in request object.
[2712]133
[2722]134Get the current user based on environment variables set by SSL or
135GSSAPI, and store it in the attribute cherrpy.request.login.
136
137Per the CherryPy API (http://www.cherrypy.org/wiki/RequestObject#login),
138the attribute is set to the username on successful login, to False on
139failed login, and is left at None if the user attempted no authentication.
140"""
[2670]141    environ = cherrypy.request.wsgi_environ
142    user = environ.get('REMOTE_USER')
143    if user is None:
144        return
145    if environ.get('AUTH_TYPE') == 'Negotiate':
146        # Convert the krb5 principal into a krb4 username
147        if not user.endswith('@%s' % config.kerberos.realm):
[2722]148            cherrypy.request.login = False # failed to log in
[2670]149        else:
150            cherrypy.request.login = user.split('@')[0].replace('/', '.')
151    else:
152        cherrypy.request.login = user
153
[2724]154cherrypy.tools.remote_user_login = cherrypy.Tool('on_start_resource',
155                                                 remote_user_login, priority=50)
[2670]156
[2723]157
[2669]158def invirtwebstate_init():
159    """Initialize the cherrypy.request.state object from Invirt"""
[2690]160    if not hasattr(cherrypy.request, "state"):
161        cherrypy.request.state = State(cherrypy.request.login)
[2669]162
[2724]163cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource',
164                                              invirtwebstate_init, priority=100)
[2669]165
[2723]166
[2993]167cherrypy.tools.clear_db_cache = cherrypy.Tool('on_start_resource', invirt.database.clear_cache)
168
169
[2659]170class View(object):
[2724]171    _cp_config = {'tools.mako.directories':
172                      [os.path.join(os.path.dirname(__file__),'templates')]}
Note: See TracBrowser for help on using the repository browser.