Dan Milstein
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.
Let's say the specification for overall, site-wide authorization looks something like the following:
Users have to log in in order to view the site...
...except for content rooted at /public, which is openly accessible
If a user has logged in, there will be a user_id stored in the session
If a non-logged in user attempts to access a non-public page, they should
be redirected to /Login
After logging in, such a user should be taken to the page they originally requested
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
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).
In Application.config, configure the FilterMappings setting as so:
FilterMappings = [ (r'/', AuthorizerFilter) ]
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.
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:
Non-servlet content won't also be protected (text files, PDF's, etc).
You have to remember to inherit from that base servlet, and if you forget, you'll create an easily-overlooked security hole.
A change in authorization policy can mean a change in the source code for many servlets.
If there are multiple path-based actions implemented through inheritance, a developer reading the source of a servlet will have to back up through a potentially lengthy series of classes to understand the whole chain. With filters, the overall behavior is specified in one place.