Filters in Webware

Dan Milstein

Overview

Filters give you a simple, clean way to specify your own path-based request-handling pipeline. They are useful for authorization, for URL manipulation, for logging, and for many other situations.

An Authorization example:

Let's say the specification for overall, site-wide authorization looks something like the following:

The Code

  1. Create an AuthorizerFilter module with a class named AuthorizerFilter, as follows:

    from WebKit.Filter import Filter
    
    import re
    
    class AuthorizerFilter(Filter):
    
        ignore_path_re = re.compile(r'^/Login$|^/public')
    
        def filter(self, trans, next):
            path = trans.request().pathInfo()
            if self.ignore_path_re.search(path):
                next(trans)
            else:
                sess = trans.session()
                if 'user_id' in sess:
                    next(trans)
                else:
                    uri = trans.request().uri()            
                    sess['requested_url'] = uri
                    trans.response().sendRedirect("/Login")
                    return None
    
  2. Put it somewhere that Webware can see as the root of the python package tree

    It does not need to be in same directory as your servlets (and, in fact, I'd recommend against that, so that Webware can't try to load and run them as servlets).

  3. In Application.config, configure the FilterMappings setting as so:

    FilterMappings = [ (r'/', AuthorizerFilter) ]
    

How It Works

When Webware handles a request, it looks at the list of FilterMappings, creates a regex from the first part of each tuple, and attempts to match those regexes to the URL. For every match, it creates a Filter instance from the class listed in the second part of the tuple. It collects all matching filters into a chain.

Note that the order of that chain depends on the order the FilterMappings are listed in Application.config. In this example, there's just one, but, in general, the order is very important.

Say the user had requested:

/private/accounts/ViewAccountDetails

In our example, Webware would see that this URL matches the regex of the very first FilterMapping -- '/'. So it would construct a chain of filters which looks like:

[ AuthorizerFilter() ]

It then constructs a special next object, and calls the authorizer's filter(trans, next) method.

The AuthorizerFilter first checks to see if it should ignore the request (is it the public part of the site, or the login page itself?). If not, it checks to see if the user has credentials in the session. If so, it forwards the request on to the rest of the chain (via the call to next(trans)). In this case, there are no other filters, so that returns control to Webware, which will look up the servlet associated with /private/accounts/ViewAccountDetails, and execute it.

If the user does not have credentials in the session, AuthorizerFilter will store the requested URL in the session and send the user off to Login. In that case, the filter does not call next, so there is no further processing, either by other filters, or by Webware itself.

Note that if our filter had modified the path, then that would help determine what servlet is ultimately run by Webware. This makes it possible to rewrite the URL in useful ways.

Why Not Use Inheritance?

Now, you could set it up so that all your servlets in the non-public directory inherit from some password-checking base servlet, but that has the following disadvantages: