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