[TransWarp] Authentication services, specialists, and rules-checking

Phillip J. Eby pje at telecommunity.com
Thu Aug 7 17:30:48 EDT 2003

I've been working the last day or two on authentication services for 
peak.web, and have run into some interesting hitches.  The most basic 
functions of an authentication service are easily met, you just need 
something that pulls data out of a request and looks up a user, no real 

However, although identifying the current user is a straightforward 
computation-only task, there is a wide assortment of related services that 
are also needed.  For example, a login form, and a logout method.  There 
may also be needs to manage users, register a new member and so on.

In principle, these needs are part of the application, and should be 
defined as part of the application's presentation components.  But, it may 
be that the authentication service needs to access them.  For example, 
maybe the right behavior is for the authentication service to present the 
login screen when there's invalid authentication data found.

As I think this over, I'm realizing that this isn't quite right.  The 
authentication service can just raise an error, which can then be adapted 
to whatever behavior is desired by the application.  And, different 
services might have different errors that they raise.  For example, an auth 
service with policies like those of Yahoo, might raise a "your login has 
timed out" error, which would then be adapted to a "please re-enter your 
password" screen.  (Technically, it probably wouldn't work this way, and 
thinking about how to do it correctly actually leads to some other 
interesting thoughts I'll explore later in this message.)

For other operations, like logging a user out, signalling credential 
changes and such, it's more than adequate to use the convenient, centrally 
located authentication service.  However, for services that a user needs to 
access via the web, the authentication service can't be used.  So, we end 
up needing a "user specialist" component that has a known URL within the 
site, in order to have e.g. a '/logout' method.

This isn't such a big deal, but it does make me wonder then, how other 
components in the site will find it.  For most of our current applications, 
we just use '/acl_users' or some such, per Zope, and it's a hardcoded 
path.  But I've been saying for some time now that hardcoded paths make for 
poor modularity.  It seems to me that there should be some way to access 
specialists, in the same way that we can DMs.  Thus, presentation 
components would be more reusable between sites.  To do this, a Decorator 
would just bind to a Specialist, and the Specialist would have to be 
available from some parent Decorator.  In a template, one can refer to the 
specialist relative to the current decorator, and use a "url" DOMlet to 
point it to its "true" URL, so long as the Specialist is a "Resource" in 
our current terminology.  Thus, we need service-oriented Decorator classes 
that know how to ensure their absolute URL-ness, even if accessed via 
another object.

Backing up a little bit to the error handling thing, I'm thinking that 
probably authentication services should be configurable as to what errors 
they use, and/or that the NotAllowed error may need to be more 
fine-grained.  Right now, NotAllowed just means that you didn't have the 
access needed to do what you wanted to do.  But, there is no way to 
parameterize that, to say that you're not allowed because for example, your 
session expired, versus, you're just not allowed, period.

At issue here is the fact that interaction.allows() communicates only via a 
true-or-false return value, so there's no way to get the details, even if 
the business rules that interpret the permissions know exactly what's going 
on.  Further, the current traversal machinery just uses NOT_FOUND and 
NOT_ALLOWED as sentinel values, so there's no data there, either.

So I wonder: should rules raise errors, instead of just a yes/no 
response?  Certainly, this would allow them to communicate in very 
application-specific terms about the nature of the issue.  If part of the 
required interface for such errors was to provide a method that would 
provide alternate data for templates trying to access the protected data, 
then we could perhaps allow DOMlets to catch these errors and render the 
alternate content, similar to certain modules in My Yahoo displaying "you 
must log in to access this feature" when you haven't entered your password 
in a while, and meanwhile the news modules and such would still display 
their (public) contents.

Granted, I do have some qualms about throwing exceptions for this.  For 
example, if more than one permission guards an object, how do you determine 
which exception should be used/displayed?  I suppose another useful 
approach would be to have interaction.allows() return the failed "attempt" 
object, which would have a false value, but contain a log of information 
about why the attempt failed.  OTOH, this seems like maybe *too much* 
information, especially if you're paranoid about security.  (Of course, I 
suppose if you're paranoid, you won't put that kind of information 
disclosure into your security rules.)

Hm.  As I think about how we might try to deal with such "logs of failing 
things", I begin to think two things: 1) maybe exceptions would be better, 
and 2) we could simply require that permissions always be 
singular.  Instead of saying permissions=[Worker,Manager], we might want to 
say permission=WorkerOrManager, and encapsulate the "or" within the 
permission rule.  Then, it could say, e.g. "You must be a worker or manager 
within this facility", instead of issuing two errors for, "You must be a 
worker" and "You must be a manager", which makes little sense to the 
user.  Meanwhile, permissions that "and" two other permissions can simply 
return the result from the permission that failed first.

I still don't really want interaction.allows() to throw an error, though, 
all things considered.  It would probably make more sense for 
'contextFor()' or 'traverseTo()' to throw the error.  The idea here is that 
we do want an error, so that code using a traversal knows that the 
traversal it's on is broken.  It doesn't really make sense to return a 
valid new traversal context that simply points to a special "not found" or 
"not allowed" place, because then you might have a template that keeps on 
going and just coincidentally appears to work.

Incidentally, this actually is better for error handling in templates, 
because it can be made explicit.  Right now, DOMlets just (explicitly) 
ignore missing or unauthorized data.  It would be better to let them 
implicitly ignore it, but also be able to explicitly handle it, e.g. by 
letting you specify the granularity at which a set of items is 
required.  For example, if you lack access to one field of the record, 
should you be denied access to just the field, or the entire record?  Maybe 
the whole list of records?  Being able to handle the error at the level of 
some useful block structure is better for things like the Yahoo "you need 
to login to use this module", as it makes no sense for the module to write 
this once for every record it would have shown!

In order to do this, of course, there would need to be an explicit 
"catcher" DOMlet specified at some level.  The catcher would supply a new 
state to its children, that wrote their output to a temporary buffer.  In 
case of an error, the catcher would attempt to adapt the error to something 
that could be displayed in place of the original contents, and write that 
instead.  If there was no error, it would write the buffered output to its 
parent stream.

So, for something like My Yahoo, each module's content block would be 
wrapped with a catcher that would revert to giving an error message as soon 
as a contained, non-error-handling DOMlet tried to access the protected 
content.  But, if all the content was accessible, it would be written out 
and processing would proceed.

So, to go back and try to summarize...

* We need "absolute location" decorator classes for services like Specialists

* There needs to be some kind of security.Denial("message") object that has 
a false value but can also be converted to a string/unicode value, and 
maybe can be raised as an exception.

* permissionsNeeded should become permissionNeeded, throughout all of 
PEAK.  Permissions like "ThisOrThat", can be implemented by rules that 
simply return 'attempt.allows(This) or attempt.allows(That) or 
security.Denial("You need this or that").

* IWebTraversable.traverseTo() should accept a context rather than an 
interaction, and throw NotAllowed(context, denial) instead of returning 
NOT_ALLOWED, when an access attempt is denied.  Similarly, it should throw 
NotFound(context) when something isn't found.  Special handling for 
NOT_FOUND and NOT_ALLOWED should just go away altogether.  This 
unfortunately means that certain traversables such as Decorator and 
MultiTraverser will now need some extra exception handling to accomplish 
their tasks, as will interaction.getDefaultTraversal.  But, I think this 
will save a lot of duplicated effort in DOMlet classes, and I think there 
will be a lot more custom DOMlet classes than there will be custom 
traversables that need special treatment for these not allowed/not found 

* There needs to be some kind of "catch" DOMlet, although I don't know what 
it should be called.  Maybe "try"?  There's also something of a question as 
to what its default handling for errors should be.  I suppose we could have 
a 'templateErrorProtocol' on the interaction, to which the error instance 
is adapted, but then I wonder whether an individual DOMlet might want 
specific configuration for this.  Perhaps we should just make the target 
protocol a binding on the DOMlet, and let people with custom needs subclass 
it.  That might be simplest.  Indeed, perhaps we could just make 
IWebException include a method for being rendered *within* a template, as 
opposed to *instead of* a template.  That would let you specify both 
behaviors for an error in one place.

Anyway, as always, your thoughts, questions, suggestions, comments, etc. 
are welcome.

More information about the PEAK mailing list