[Twisted-web] Get identity of previously-authenticated user

Glyph Lefkowitz glyph at twistedmatrix.com
Fri Apr 22 07:27:31 EDT 2011


On Apr 22, 2011, at 6:51 AM, Phil Mayers wrote:

> On 04/22/2011 01:13 AM, Glyph Lefkowitz wrote:
>> 
>> On Apr 21, 2011, at 11:12 AM, Mike Pelletier wrote:
>> 
>>> The only opportunity I have to bind a user identity/object to a
>>> request is by constructing and throwing away a new Resource tree for
>>> each request in requestAvatar. I decided that was probably not what I
>>> was meant to do. Instead, I have done something that turns out to
>>> smell very bad to solve this problem. What was the correct solution?
>> 
>> Resources can be extremely lightweight objects. If your actual resource
>> tree is heavyweight (containing lots of data, shared caches, etc) you
>> can trivially build an overlay resource tree that just remembers the
>> user object and binds it to the underlying tree.
>> 
>> So the main question is: why did you decide that this is not what you
>> were meant to do?
> 
> For what it's worth: I decided exactly the same thing way back when, and 
> also discovered that, no, you are supposed to do that.

Thanks for that data point.

> I thought about why I'd decided that, and came to the conclusion it's 
> just not intuitive - it "felt" wrong - but couldn't be more specific. 
> Possibly it's because almost every other web framework I've ever seen, 
> python or not, gives you a "user" object of some description on the request?

This always seemed to me to be an incredibly bad design, copied from the era of CGI because nobody had a better idea.  Except of course Zope did have a better idea (object publishing), but obscured it with some really terrible ones (authentication by acquisition) so it was not generally recognized as good.

> Perhaps a note in the web/guard docs suggesting this approach might tip 
> people off.

Yeah, erm, hrm.  The cred documentation doesn't really get this across very well.  I'd like to say I'd update the documentation myself, but honestly, the way cred (and guard) works seems so completely obvious to me that it's hard for me to express this well to people with a more traditional web background (or even non-web stuff with more traditional authentication APIs).

I was exposed to capability security and authentication patterns very early in my programming career, so "that thing everybody does where they pass a user object to everything" was first explained to me in explicit terms as "the common way in which people implement easily broken security" rather than in some framework's documentation as a good idea.

The idea with cred-based authentication of all kinds (of which twisted.web.guard is merely one implementation) is to do authentication by naming rather than by checking.  The traditional way of doing access control is to do something like this:

class MyFancyRootResource(object):
    def render_GET(self, request, user):
        if user.hasPermission(self, READ):
            return self.sensitiveData()
        else:
            return FourOhThree()

The cred way of doing it is to do something more like this:

class MyRealm(object):
    def requestAvatar(self, avatarID, mind, *interfaces):
        avatar = self.lookup(avatarID)
        if avatar.canAccessSensitiveData():
            return SensitiveData()
        else:
            return FourOhThree()

At this scale, they probably seem roughly equivalent.  But imagine what happens to this code as it grows.  Maybe I want to add a 'render_POST' to my root resource.  In this idiom, I need another permission checking call.  Then maybe I want to add a getChild.  Now I need another.  And another for every method on every child.  Of course, I might forget to implement one of these methods on FourOhThree, but then all the attacker will get is a 500 (AttributeError), which is hardly more useful to them than an HTTP protocol error.

When I sit down to write render_GET, what's foremost in my mind is making sure that the page looks right and that it does what I expect when I am authenticated.  It's hard to have the level of discipline required to remind yourself after every single change or every single new feature that you might have to add one more 'if' check for security purposes.  It's much easier if your checking code is off in a different area, that you only look at when you're actually thinking about security rules, and by the time you've gotten to the application object, you implicitly have permission to use it.

This also makes testing a bit easier, since you don't need a fake user in many places that you otherwise would; if you're just testing the positive path, you can just check that the 'inner' object works as expected, without testing its interaction with the whole process of authentication.  It doesn't always help; if every page needs to display a 'hello, $USER' banner somewhere, then you can't avoid much work, but in the cases where you don't need it (access to downloadable files, for example) it is nice to be able to skip it.

I'd be happy to rant at someone and draw some diagrams on a whiteboard to try to come up with a better explanation though.

> (That said, I don't use Twisted for web stuff anymore, so it's not of 
> much interest to me)

Too bad, sorry to hear it.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://twistedmatrix.com/pipermail/twisted-web/attachments/20110422/c5b5bdb4/attachment.htm 


More information about the Twisted-web mailing list