Changelog for invirt-web 0.1.44
[invirt/packages/invirt-web.git] / code / view.py
index 87a527b..51f19a5 100644 (file)
@@ -1,17 +1,21 @@
-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 invirt.config import structs as config
+import invirt.database
 from webcommon import State
 
 from webcommon import State
 
+
 class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
 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'):
+    def __init__(self, template, next_handler,
+                 content_type='text/html; charset=utf-8'):
         self.template = template
         self.next_handler = next_handler
         self.content_type = content_type
         self.template = template
         self.next_handler = next_handler
         self.content_type = content_type
@@ -27,10 +31,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',
-                 imports=[]):
+
+    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:
@@ -45,14 +48,45 @@ class MakoLoader(object):
                                     imports=imports,
                                     )
             self.lookups[key] = lookup
                                     imports=imports,
                                     )
             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.template = t = lookup.get_template(filename)
-        cherrypy.request.handler = MakoHandler(t, cherrypy.request.handler, content_type)
+        cherrypy.request.handler = MakoHandler(
+            t, cherrypy.request.handler, content_type)
+
+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)
 
 
-main = MakoLoader()
-cherrypy.tools.mako = cherrypy.Tool('on_start_resource', main)
 
 class JSONEncoder(simplejson.JSONEncoder):
        def default(self, obj):
 
 class JSONEncoder(simplejson.JSONEncoder):
        def default(self, obj):
@@ -63,16 +97,16 @@ class JSONEncoder(simplejson.JSONEncoder):
                else:
                        return simplejson.JSONEncoder.default(self, obj)
 
                else:
                        return simplejson.JSONEncoder.default(self, obj)
 
+
 def jsonify_tool_callback(*args, **kwargs):
     if not cherrypy.request.cached:
         response = cherrypy.response
         response.headers['Content-Type'] = 'text/javascript'
         response.body = JSONEncoder().iterencode(response.body)
 
 def jsonify_tool_callback(*args, **kwargs):
     if not cherrypy.request.cached:
         response = cherrypy.response
         response.headers['Content-Type'] = 'text/javascript'
         response.body = JSONEncoder().iterencode(response.body)
 
-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."""
 
 def require_login():
     """If the user isn't logged in, raise 403 with an error."""
@@ -80,33 +114,61 @@ def require_login():
         raise cherrypy.HTTPError(403,
             "You are not authorized to access that resource")
 
         raise cherrypy.HTTPError(403,
             "You are not authorized to access that resource")
 
-cherrypy.tools.require_login = cherrypy.Tool('on_start_resource', require_login, priority=150)
+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")
+    if not cherrypy.request.headers.get('Referer', '').startswith('https://' + config.web.hostname):
+        raise cherrypy.HTTPError(403, "This form is only usable when submitted from another page on this site. If you receive this message in error, check your browser's Referer settings.")
+
+cherrypy.tools.require_POST = cherrypy.Tool('on_start_resource',
+                                            require_POST, priority=150)
+
 
 def remote_user_login():
 
 def remote_user_login():
-    """Get the current user based on the SSL or GSSAPI environment variables"""
+    """Get remote user from SSL or GSSAPI, and store in request object.
+
+Get the current user based on environment variables set by SSL or
+GSSAPI, and store it in the attribute cherrpy.request.login.
+
+Per the CherryPy API (http://www.cherrypy.org/wiki/RequestObject#login),
+the attribute is set to the username on successful login, to False on
+failed login, and is left at None if the user attempted no authentication.
+"""
     environ = cherrypy.request.wsgi_environ
     user = environ.get('REMOTE_USER')
     if user is None:
         return
     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):
     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
+            cherrypy.request.login = False # failed to log in
         else:
             cherrypy.request.login = user.split('@')[0].replace('/', '.')
     else:
         cherrypy.request.login = user
 
         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)
+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.clear_db_cache = cherrypy.Tool('on_start_resource', invirt.database.clear_cache)
 
 
-cherrypy.tools.invirtwebstate = cherrypy.Tool('on_start_resource', invirtwebstate_init, priority=100)
 
 class View(object):
 
 class View(object):
-    _cp_config = {'tools.mako.directories': [os.path.join(os.path.dirname(__file__),'templates')]}
+    _cp_config = {'tools.mako.directories':
+                      [os.path.join(os.path.dirname(__file__),'templates')]}