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.

_______________________________________________
Twisted-web mailing list
[email protected]
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web

Reply via email to