Clear all objects from the SQLAlchemy session at the start of each request.
[invirt/packages/invirt-web.git] / code / view.py
1 import os, sys
2
3 import cherrypy
4 from mako.template import Template
5 from mako.lookup import TemplateLookup
6 import simplejson
7 import datetime, decimal
8 from StringIO import StringIO
9 from invirt.config import structs as config
10 import invirt.database
11 from webcommon import State
12
13
14 class 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
30 class 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
62 cherrypy.tools.mako = cherrypy.Tool('on_start_resource', MakoLoader())
63
64
65 def 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
75 def 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
88 cherrypy.tools.catch_stderr = cherrypy.Tool('before_handler', catchStderr)
89
90
91 class 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
101 def 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
107 cherrypy.tools.jsonify = cherrypy.Tool('before_finalize',
108                                        jsonify_tool_callback, priority=30)
109
110
111 def 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
117 cherrypy.tools.require_login = cherrypy.Tool('on_start_resource',
118                                              require_login, priority=150)
119
120
121 def 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
127 cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource',
128                                             require_POST, priority=150)
129
130
131 def remote_user_login():
132     """Get remote user from SSL or GSSAPI, and store in request object.
133
134 Get the current user based on environment variables set by SSL or
135 GSSAPI, and store it in the attribute cherrpy.request.login.
136
137 Per the CherryPy API (http://www.cherrypy.org/wiki/RequestObject#login),
138 the attribute is set to the username on successful login, to False on
139 failed 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
154 cherrypy.tools.remote_user_login = cherrypy.Tool('on_start_resource',
155                                                  remote_user_login, priority=50)
156
157
158 def 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
163 cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource',
164                                               invirtwebstate_init, priority=100)
165
166
167 cherrypy.tools.clear_db_cache = cherrypy.Tool('on_start_resource', invirt.database.clear_cache)
168
169
170 class View(object):
171     _cp_config = {'tools.mako.directories':
172                       [os.path.join(os.path.dirname(__file__),'templates')]}