On Apr 13, 2008, at 3:49 AM, Michael Weber wrote:
I actually find it overly flexible. (This is perhaps another of the "organic growth" areas.) There are several ways to plug into the dispatcher. At the moment, I am using this:
Yes, I think this is part of the challenge here. There are many ways to plug the sort of functionality I'm thinking of into hunchentoot. I'm not satisfied with the current approach taken by hunchentoot-auth and the myriad of choices for plugging this stuff in is what initially precipitated this discussion.
At the risk of taking this discussion from the philosophical to the practical, allow me to discuss some options for wedging in the authorization stuff.
+ authorized-page macro
The existing approach is an authorized-page -style macro that wraps each page and does the following:
* check if an https connection is required and if so that we're actually using an https connection otherwise, redirect to an https page on the appropriate port. * either check that the supplied user and password are correct or that the user's session was properly authenticated. If necessary, squirrel away the user name in a per-session hash-table and set a flag that in a per-session hash-table that the user is authenticated.
The disadvantage of this approach is that it requires wrapping the code that generates each page with the authorized-page macro. One can't take arbitrary request handling code and make the page require authorization without somehow wrapping it, or another function that calls it, with this macro.
+ *meta-dispatcher*
One could rig up *meta-dispatcher* such that it checked for authorization and possibly redirect things along the way. The problem with this is that there is only one meta-dispatcher, so you only get to do this once per hunchentoot instance.
And, of course, one could override the value of *dispatch-table*, which is what the default *meta-dispatcher* returns.
+ server-dispatch-table
Similarly to the case of *meta-dispatcher*, one could use the dispatch- table slot of the server instance to hijack the dispatch and check for authentication. But it's not clear to me how the elements of this table should be ordered.
Interestingly, there's a dispatch-request generic function that could be used with a suitably defined class.
Clearly there's plenty of rope for extensibility here, the challenge, for hunchentoot-auth at least, is figuring which of these hooks to exploit.
(defvar *toplevel-routing-table* (let ((rt (make-instance 'ht-routing-table))) (shiftf (get-routes rt) hunchentoot:*dispatch-table* rt) rt))
(defmethod hunchentoot:dispatch-request ((table routing-table)) (let ((controller (find-controller table *request*))) (handle-request controller *request*)))
However, another option for me would be to just push
(lambda () (hunchentoot:dispatch-request *toplevel-routing-table* *request*))
onto hunchentoot:*dispatch-table*. And I haven't even look at the meta-dispatcher stuff and starting multiple server instances.
Right. Multiple server-instances is another issue and it becomes important for what i'm doing because I use two server instances, one for http and one for https. There's no built-in infrastructure for managing multiple "servers". One could imagine some sort of meta- server (or renaming the server class to a listener and allowing for the server to have multiple listeners, but I guess that's just a nomenclature issue).
There's probably a way to simplify all this without losing any power or convenience.
Right.
Cheers, Michael BTW: The reasons behind all this:
- I like the mappings between URLs and handlers a little more
descriptive than bare function designators, for example, to print out the mapping or appropriate Apache config stanzas. So I use CLOS objects. Alternatively, I could have used (:metaclass funcallable- standard-class).
I agree. I like having more of the "metadata" kept with the function too.
- I like to be able to rearrange URL mappings while running in
development. (make-prefix-matcher "/foo/") is a little too static for my taste.
- I bundle several end points (handlers) together (into a
"controller"), because on their own, they don't make sense. Also, the end points don't know anything about the URL they are mapped to.
- I can deploy a single controller several times on different URL
routes (e.g., "/~foo/...", "/~bar/...", etc.). The routing dissects the URL and provides parameters to controller and end points. Deploying multiple "web apps" comes for free.
- Authentication is done by Apache, for the moment, because it's
convenient and works for files served statically, too.
Hmm... I've taken the perhaps crazy approach of using ht for everything, static files, CGI scripts, etc...
- Authorization is done by Apache and by controllers (for, say, DB
access), because all end points are usually subject to the same rules. End points can do additional checks with finer granularity.
Thanks for your comments,
Cyrus