Chris,

Thanks for the quick response -- I didn't expect to go in 0.9.2 -- no worries about that.

Two things:

1) I *did* screw up the tabs/spaces in one of the files - FilterChain.py.

I've attached a fixed version. Sorry. I've got Emacs set up to use spaces by default, and going back and forth is, um, tricky. Here's hoping (but not expecting) that this is the last such mistake.


2) Attached is the start of a tutorial/how-to style doc on using filters

I'm attaching it in rough form to a) give a teaser for folks on this list who want to try filters out, and b) get feedback on whether or not this is a useful kind of doc. I'll keep working on this one, but if people are feeling like it's too simple, or would work better as part of a general Webware tutorial, let me know.

-Dan

from Common import *


class FilterChain(Object):
	"""

	A FilterChain holds the specific, ordered list of Filters which are
	found to apply to a given request.	They participate in transactions.
	
	XXX NOT IMPLEMENTED YET XXX
	It is intended that FilterChains can be created once, then used and
	destroyed, or they may be reused several times over (it's up to the
	server). 

	Objects that participate in a transaction include:
	
	* Application
	* Request
	* Transaction
	* Session
	* Servlet
	* Response
	* FilterChain

	The awake(), respond() and sleep() methods form a message
	sandwich. Each is passed an instance of Transaction which gives further
	access to all the objects involved.
	"""

	## Init ##
	
	def __init__(self):
		""" Subclasses must invoke super. """
		Object.__init__(self)
		self._serverSidePath = None
		self._filters = []


	## Access ##
	
	def name(self):
		""" Returns the name which is simple the name of the class.
		Subclasses should *not* override this method. It is used for
		logging and debugging. """
		return self.__class__.__name__


	## Filter Creation ##
	def addFilter(self, filter):
		self._filters.append(filter)

	## Request-response cycles ##

	def awake(self, trans):
		""" This message is sent to all objects that participate in the
		request-response cycle in a top-down fashion, prior to respond()."""
		self._transaction = trans
		for f in self._filters:
			f.awake(trans)

	def respond(self, trans):
		filters = self._filters
		next = Next(filters)
		first = filters[0]
		first.filter(trans, next)

	def sleep(self, trans):
		for f in self._filters:
			f.sleep(trans)
		#del self._filters


	## Abilities ##

	def canBeThreaded(self):
		""" Returns 0 or 1 to indicate if the servlet can be multithreaded. This value should not change during the lifetime of the object. The default implementation returns 0. Note: This is not currently used. """
		return 0

	def canBeReused(self):
		""" Returns 0 or 1 to indicate if a single servlet instance can be
		reused. The default is 1, but subclasses con override to return
		0. Keep in mind that performance may seriously be degraded if
		instances can't be reused. Also, there's no known good reasons not
		to reuse and instance. Remember the awake() and sleep() methods are
		invoked for every transaction. But just in case, your servlet can
		refuse to be reused."""
		return 1


	## Server side filesystem ##

	def serverSidePath(self, path=None):
		""" Returns the filesystem path of the page on the server. """
		if self._serverSidePath is None:
			if hasattr(self, "_request") and self._request is not None:
				self._serverSidePath = self._request.serverSidePath()
			else:
				self._serverSidePath = self._transaction.request().serverSidePath()
		if path:
			return os.path.normpath(os.path.join(os.path.dirname(self._serverSidePath), path))
		else:
			return self._serverSidePath


	## Cleanup ##
	
	def clearTransaction(self):
		del self._transaction



class Next(Object):

	def __init__(self, chain, curIdx = 0):
		Object.__init__(self)
		self._chain = chain
		self._curIdx = curIdx

	def __call__(self, trans):
		nextIdx = self._curIdx + 1
		chain = self._chain
		if nextIdx > len(chain):
			raise Exception("next called past end of FilterChain: %s" % chain)
		nextFilter = chain[nextIdx]
		nextNext = Next(chain, nextIdx)
		nextFilter.filter(trans, nextNext)
	

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:

  • 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

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:

  • 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.


On Sep 17, 2006, at 5:07 PM, Christoph Zwerschke wrote:

Dan Milstein wrote:
I've added java servlet-style filters to Webware, and would like to
contribute the code to the project, if people are interested.
...
If people are interested, I can write up some documentation.

That's surely interesting, thanks.

I'll have a look at it and I'd also like to hear feedback from others;
maybe we can include it with 0.9.3.  For 0.9.2 it's unfortunately too
late, I don't want to defer it again.

Instead of only documentation, it would be nice to have some usage
examples too, maybe a tutorial on building a small application using
these and other Webware features.

-- Chris

---------------------------------------------------------------------- --- Using Tomcat but need to do more? Need to support web services, security? Get stuff done quickly with pre-integrated technology to make your job easier Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo http://sel.as-us.falkag.net/sel? cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Webware-devel mailing list
Webware-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/webware-devel

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Webware-devel mailing list
Webware-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/webware-devel

Reply via email to