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