[TransWarp] Authentication services, specialists, and rules-checking
Roché Compaan
roche at upfrontsystems.co.za
Fri Aug 8 02:32:09 EDT 2003
* Phillip J. Eby <pje at telecommunity.com> [2003-08-07 23:50]:
> 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
> problem.
>
> 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.
Sounds great!
> 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.
This made me wonder how conditions in general translate to DOMlets. In
TAL you can specify tal:condition="some condition". Probably as simple
as having a "condition" DOMLet which replace itself and its contents
with nothing if the condition returns false:
<li define="condition:foo">show foo</li>
<li define="condition:bar">show bar</li>
> 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.
Very cool. How will one introspect permissions? It's easy to say:
if "Worker" in permissions: ...
but how would you do that with WorkerOrManager?
> 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
> conditions.
>
> * There needs to be some kind of "catch" DOMlet, although I don't know what
> it should be called. Maybe "try"?
"try" is good.
> 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.
How is it determined which is rendered when an error occurs?
--
Roché Compaan
Upfront Systems http://www.upfrontsystems.co.za
More information about the PEAK
mailing list