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

Last change on this file since 3018 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
Line 
1import os, sys
2
3import cherrypy
4from mako.template import Template
5from mako.lookup import TemplateLookup
6import simplejson
7import datetime, decimal
8from StringIO import StringIO
9from invirt.config import structs as config
10import invirt.database
11from webcommon import State
12
13
14class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
15    """Callable which processes a dictionary, returning the rendered body."""
16   
17    def __init__(self, template, next_handler,
18                 content_type='text/html; charset=utf-8'):
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 = {}
34
35    def get_lookup(self, directories, module_directory=None,
36                     collection_size=-1, imports=[], **kwargs):
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',
48                                    imports=imports,
49                                    )
50            self.lookups[key] = lookup
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=[]):
56        cherrypy.request.lookup = lookup = self.get_lookup(
57            directories, module_directory, collection_size, imports)
58        cherrypy.request.template = t = lookup.get_template(filename)
59        cherrypy.request.handler = MakoHandler(
60            t, cherrypy.request.handler, content_type)
61
62cherrypy.tools.mako = cherrypy.Tool('on_start_resource', MakoLoader())
63
64
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
74
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
90
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
100
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
107cherrypy.tools.jsonify = cherrypy.Tool('before_finalize',
108                                       jsonify_tool_callback, priority=30)
109
110
111def require_login():
112    """If the user isn't logged in, raise 403 with an error."""
113    if cherrypy.request.login is False:
114        raise cherrypy.HTTPError(403,
115            "You are not authorized to access that resource")
116
117cherrypy.tools.require_login = cherrypy.Tool('on_start_resource',
118                                             require_login, priority=150)
119
120
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
127cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource',
128                                            require_POST, priority=150)
129
130
131def remote_user_login():
132    """Get remote user from SSL or GSSAPI, and store in request object.
133
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"""
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):
148            cherrypy.request.login = False # failed to log in
149        else:
150            cherrypy.request.login = user.split('@')[0].replace('/', '.')
151    else:
152        cherrypy.request.login = user
153
154cherrypy.tools.remote_user_login = cherrypy.Tool('on_start_resource',
155                                                 remote_user_login, priority=50)
156
157
158def invirtwebstate_init():
159    """Initialize the cherrypy.request.state object from Invirt"""
160    if not hasattr(cherrypy.request, "state"):
161        cherrypy.request.state = State(cherrypy.request.login)
162
163cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource',
164                                              invirtwebstate_init, priority=100)
165
166
167cherrypy.tools.clear_db_cache = cherrypy.Tool('on_start_resource', invirt.database.clear_cache)
168
169
170class View(object):
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.