"Cherry-pick" r2557 (Re-arrange the authz configuration.) to the
[invirt/packages/invirt-web.git] / code / view.py
index 1a63eda..1b586ef 100644 (file)
@@ -1,15 +1,17 @@
-import os
+import os, sys
 
 import cherrypy
 from mako.template import Template
 from mako.lookup import TemplateLookup
 import simplejson
 import datetime, decimal
 
 import cherrypy
 from mako.template import Template
 from mako.lookup import TemplateLookup
 import simplejson
 import datetime, decimal
+from StringIO import StringIO
 from invirt.config import structs as config
 from webcommon import State
 
 class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
 from invirt.config import structs as config
 from webcommon import State
 
 class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
-    """Callable which sets response.body."""
+    """Callable which processes a dictionary, returning the rendered
+    body."""
     
     def __init__(self, template, next_handler, content_type='text/html; charset=utf-8'):
         self.template = template
     
     def __init__(self, template, next_handler, content_type='text/html; charset=utf-8'):
         self.template = template
@@ -27,9 +29,9 @@ class MakoLoader(object):
     
     def __init__(self):
         self.lookups = {}
     
     def __init__(self):
         self.lookups = {}
-    
-    def __call__(self, filename, directories, module_directory=None,
-                 collection_size=-1, content_type='text/html; charset=utf-8'):
+
+    def get_lookup(self, directories, module_directory=None,
+                     collection_size=-1, imports=[], **kwargs):
         # Find the appropriate template lookup.
         key = (tuple(directories), module_directory)
         try:
         # Find the appropriate template lookup.
         key = (tuple(directories), module_directory)
         try:
@@ -41,16 +43,44 @@ class MakoLoader(object):
                                     default_filters=['decode.utf8'],
                                     input_encoding='utf-8',
                                     output_encoding='utf-8',
                                     default_filters=['decode.utf8'],
                                     input_encoding='utf-8',
                                     output_encoding='utf-8',
+                                    imports=imports,
                                     )
             self.lookups[key] = lookup
                                     )
             self.lookups[key] = lookup
-        cherrypy.request.lookup = lookup
-        
-        # Replace the current handler.
+        return lookup
+
+    def __call__(self, filename, directories, module_directory=None,
+                 collection_size=-1, content_type='text/html; charset=utf-8',
+                 imports=[]):
+        cherrypy.request.lookup = lookup = self.get_lookup(directories, module_directory,
+                                                           collection_size, imports)
         cherrypy.request.template = t = lookup.get_template(filename)
         cherrypy.request.handler = MakoHandler(t, cherrypy.request.handler, content_type)
 
         cherrypy.request.template = t = lookup.get_template(filename)
         cherrypy.request.handler = MakoHandler(t, cherrypy.request.handler, content_type)
 
-main = MakoLoader()
-cherrypy.tools.mako = cherrypy.Tool('on_start_resource', main)
+cherrypy.tools.mako = cherrypy.Tool('on_start_resource', MakoLoader())
+
+def revertStandardError():
+    """Move stderr to stdout, and return the contents of the old stderr."""
+    errio = sys.stderr
+    if not isinstance(errio, StringIO):
+        return ''
+    sys.stderr = sys.stdout
+    errio.seek(0)
+    return errio.read()
+
+def catchStderr():
+    old_handler = cherrypy.request.handler
+    def wrapper(*args, **kwargs):
+        sys.stderr = StringIO()
+        ret = old_handler(*args, **kwargs)
+        e = revertStandardError()
+        if e:
+            if isinstance(ret, dict):
+                ret["error_text"] = e
+        return ret
+    if old_handler:
+        cherrypy.request.handler = wrapper
+
+cherrypy.tools.catch_stderr = cherrypy.Tool('before_handler', catchStderr)
 
 class JSONEncoder(simplejson.JSONEncoder):
        def default(self, obj):
 
 class JSONEncoder(simplejson.JSONEncoder):
        def default(self, obj):
@@ -69,20 +99,54 @@ def jsonify_tool_callback(*args, **kwargs):
 
 cherrypy.tools.jsonify = cherrypy.Tool('before_finalize', jsonify_tool_callback, priority=30)
 
 
 cherrypy.tools.jsonify = cherrypy.Tool('before_finalize', jsonify_tool_callback, priority=30)
 
-def external_remote_user_login():
-    pass
-
 def require_login():
     """If the user isn't logged in, raise 403 with an error."""
     if cherrypy.request.login is False:
         raise cherrypy.HTTPError(403,
             "You are not authorized to access that resource")
 
 def require_login():
     """If the user isn't logged in, raise 403 with an error."""
     if cherrypy.request.login is False:
         raise cherrypy.HTTPError(403,
             "You are not authorized to access that resource")
 
-cherrypy.tools.require_login = cherrypy.Tool('on_start_resource', require_login)
+cherrypy.tools.require_login = cherrypy.Tool('on_start_resource', require_login, priority=150)
+
+def require_POST():
+    """If the request isn't a POST request, raise 405 Method Not Allowed"""
+    if cherrypy.request.method != "POST":
+        raise cherrypy.HTTPError(405,
+                                 "You must submit this request with POST")
+
+cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource', require_POST, priority=150)
+
+def remote_user_login():
+    """Get the current user based on the SSL or GSSAPI environment
+variables and store it in the request object's login variable. This
+conforms to the CherryPy API:
+http://www.cherrypy.org/wiki/RequestObject#login
+
+If the user is logged in successfully, cherrypy.request.login is set
+to the username. If the user failed to log in, cherrypy.request.login
+is set to False. If the user did not attempt authentication,
+cherrypy.request.login is set to None."""
+    environ = cherrypy.request.wsgi_environ
+    user = environ.get('REMOTE_USER')
+    if user is None:
+        return
+    else:
+        cherrypy.request.login = None # clear what cherrypy put there
+
+    if environ.get('AUTH_TYPE') == 'Negotiate':
+        # Convert the krb5 principal into a krb4 username
+        if not user.endswith('@%s' % config.kerberos.realm):
+            cherrypy.request.login = False # failed to login
+        else:
+            cherrypy.request.login = user.split('@')[0].replace('/', '.')
+    else:
+        cherrypy.request.login = user
+
+cherrypy.tools.remote_user_login = cherrypy.Tool('on_start_resource', remote_user_login, priority=50)
 
 def invirtwebstate_init():
     """Initialize the cherrypy.request.state object from Invirt"""
 
 def invirtwebstate_init():
     """Initialize the cherrypy.request.state object from Invirt"""
-    cherrypy.request.state = State(cherrypy.request.login)
+    if not hasattr(cherrypy.request, "state"):
+        cherrypy.request.state = State(cherrypy.request.login)
 
 cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource', invirtwebstate_init, priority=100)
 
 
 cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource', invirtwebstate_init, priority=100)