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