Update the cherrypy branch to use authz.afs.cells instead of just
[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 class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
13     """Callable which processes a dictionary, returning the rendered
14     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 def revertStandardError():
62     """Move stderr to stdout, and return the contents of the old stderr."""
63     errio = sys.stderr
64     if not isinstance(errio, StringIO):
65         return ''
66     sys.stderr = sys.stdout
67     errio.seek(0)
68     return errio.read()
69
70 def catchStderr():
71     old_handler = cherrypy.request.handler
72     def wrapper(*args, **kwargs):
73         sys.stderr = StringIO()
74         ret = old_handler(*args, **kwargs)
75         e = revertStandardError()
76         if e:
77             if isinstance(ret, dict):
78                 ret["error_text"] = e
79         return ret
80     if old_handler:
81         cherrypy.request.handler = wrapper
82
83 cherrypy.tools.catch_stderr = cherrypy.Tool('before_handler', catchStderr)
84
85 class JSONEncoder(simplejson.JSONEncoder):
86         def default(self, obj):
87                 if isinstance(obj, datetime.datetime):
88                         return str(obj)
89                 elif isinstance(obj, decimal.Decimal):
90                         return float(obj)
91                 else:
92                         return simplejson.JSONEncoder.default(self, obj)
93
94 def jsonify_tool_callback(*args, **kwargs):
95     if not cherrypy.request.cached:
96         response = cherrypy.response
97         response.headers['Content-Type'] = 'text/javascript'
98         response.body = JSONEncoder().iterencode(response.body)
99
100 cherrypy.tools.jsonify = cherrypy.Tool('before_finalize', jsonify_tool_callback, priority=30)
101
102 def require_login():
103     """If the user isn't logged in, raise 403 with an error."""
104     if cherrypy.request.login is False:
105         raise cherrypy.HTTPError(403,
106             "You are not authorized to access that resource")
107
108 cherrypy.tools.require_login = cherrypy.Tool('on_start_resource', require_login, priority=150)
109
110 def require_POST():
111     """If the request isn't a POST request, raise 405 Method Not Allowed"""
112     if cherrypy.request.method != "POST":
113         raise cherrypy.HTTPError(405,
114                                  "You must submit this request with POST")
115
116 cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource', require_POST, priority=150)
117
118 def remote_user_login():
119     """Get the current user based on the SSL or GSSAPI environment
120 variables and store it in the request object's login variable. This
121 conforms to the CherryPy API:
122 http://www.cherrypy.org/wiki/RequestObject#login
123
124 If the user is logged in successfully, cherrypy.request.login is set
125 to the username. If the user failed to log in, cherrypy.request.login
126 is set to False. If the user did not attempt authentication,
127 cherrypy.request.login is set to None."""
128     environ = cherrypy.request.wsgi_environ
129     user = environ.get('REMOTE_USER')
130     if user is None:
131         return
132     else:
133         cherrypy.request.login = None # clear what cherrypy put there
134
135     if environ.get('AUTH_TYPE') == 'Negotiate':
136         # Convert the krb5 principal into a krb4 username
137         if not user.endswith('@%s' % config.kerberos.realm):
138             cherrypy.request.login = False # failed to login
139         else:
140             cherrypy.request.login = user.split('@')[0].replace('/', '.')
141     else:
142         cherrypy.request.login = user
143
144 cherrypy.tools.remote_user_login = cherrypy.Tool('on_start_resource', remote_user_login, priority=50)
145
146 def invirtwebstate_init():
147     """Initialize the cherrypy.request.state object from Invirt"""
148     if not hasattr(cherrypy.request, "state"):
149         cherrypy.request.state = State(cherrypy.request.login)
150
151 cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource', invirtwebstate_init, priority=100)
152
153 class View(object):
154     _cp_config = {'tools.mako.directories': [os.path.join(os.path.dirname(__file__),'templates')]}