[Twisted-Python] New Guard question
Glyph Lefkowitz
glyph at twistedmatrix.com
Sat Jun 21 00:05:47 MDT 2003
On Thursday, June 19, 2003, at 02:33 PM, jml at ids.org.au wrote:
> I've been trying to figure out the new guard and cred, with the goal
> of perhaps
> switching Issues over to use guard rather than the custom auth it does
> now.
Hooray! (Subtext - everyone should do this and give feedback)
> Looking at the webhappyrealm.py example in the sandbox, it's not very
> clear
> what the returned resources represent.
Ah yes. This is due to my peculiar ... idiom, when dealing with web
login.
> Also, returning Resources seems a little weird. To me, a resource is
> whole
> page, why would I want a whole page returned?
You're going to have to remember that I still think of Twisted as a big
multiplayer game, and all this HTTP stuff is just kind of a grotty way
to display room descriptions. If you haven't already done so, please
check your assumptions about web development at the door. It's cool, I
can wait.
Okay then.
So, the idea here is that with any login to an interactive system, you
have a session. You log in, you interact with the system for a while,
and then you log out. Conceptually, there is an object on a "client"
that connects to an object on a "server". [New] Cred's model for
representing this interaction is the connection between the
AvatarAspect and the Mind (see previous email for definitions of the
contentious verbiage), which is established when
IRealm.requestAvatar(...) is called, and ends when the 'logout' method
returned from that call is called.
The AvatarAspect that is returned is expected to conform to an
interface (in this case, IResource) that has some meaning to the
protocol it's going to be talking to. In the simplest case, for
example, some cred junk that merely password protects a directory
structure, this resource can be the same for all users.
However, when the application that you're interacting with is actually
an *application*, and you're trying to mimic a "real" client program
with the browser, you want to provide custom logic for each user who
logs in.
Now to answer the question you actually asked: the Resource that
woven.guard is requesting from your Realm is supposed to represent the
UI to present to a particular user of your application. In theory,
you've got some domain data (perhaps an IssuesPerson) which represents
the user (their Persona) - you can wrap a Resource around this and then
provide web-specific functionality to communicate with your domain
objects; hooray, it is object-oriented programming for the web.
Then, this Resource is not only a "whole page", but an encapsulation of
the user's entire experience of the web component of your system.
Typically you will provide a customized top-level facade that has a
small amount of dynamic content and then references to shared objects,
such as static data on the filesystem or dynamic objects which are
calculated on each view but are the same for all users.
That was how to do things right. Now I will explain why you should not
do them wrong.
You may be wondering, "but what about the session!? I can keep this
data on a session object, can't I?" The answer is "yes, but why would
you?" Even given the fact that Session is componentized and may
provide custom, separated functionality, this is a protocol detail that
leads to unpleasant code. Consider, if you want to use woven.guard's
nifty session-negotiation feature, but also keep your user-specific
code in a session, your code will look like this:
# WHAT FOLLOWS IS AN EXAMPLE OF A MISTAKEN ASSUMPTION THAT IS
COMMON DUE TO
# HOW AWFUL THE WEB IS. PLEASE DO NOT WRITE NEW-CRED WEB
APPLICATIONS THIS
# WAY.
from twisted.web.resource import Resource
from twisted.cred.badidea import ICredSession
class MySingletonResource(Resource):
data = 'hello %s, do you like sunshine?'
def __init__(self, realm):
Resource.__init__(self)
self.realm = realm
def render(self, request):
# negotiate session
s = request.getSession()
if s is None:
return request.setupSession()
# return data
request.setHeader('content-type', 'text/plain')
c = s.getComponent(ICredSession)
if c is None:
return 'it looks like you are anonymous please log in!'
else:
avatar = c.getLoggedInForRealm(self.realm)
return self.data % avatar.username
Now, isn't that ugly? Granted, the guard-style session negotiation
makes it slightly more perverse, but regardless, there will be
boilerplate in every render() and getChild() for every resource you
have in order to identify the appropriate user, plus code for
Anonymous, unless you make your resources depend upon a specifc
execution context to work.
If this seems normal to you, or not particularly unpleasant, have a
look at some sample code for the suggested approach:
# THIS, ON THE OTHER HAND, IS NOT SO BAD.
from twisted.web.resource import Resource
class MyResource(Resource):
data = 'hello %s, do you like sunshine?'
def __init__(self, avatar):
Resource.__init__(self)
self.avatar = avatar
def render(self, request):
request.setHeader('content-type', 'text/plain')
return self.data % self.avatar.username
class MyAnonymousResource(Resource):
def render(self, request):
return 'it looks like you are anonymous please log in!'
I mentioned games at the start of the email. This sort of approach is
important in a game system because you want to perform on-the-fly
updates of the user's interface without necessarily altering the state
of their avatar object. Giving every user's connection its own
resource is much more flexible. Even in this small example, we've
exposed some small customizability by accident: the realm could
maintain an active pool of MyResource instances and set self.data =
'Sorry %s, *** YOU HAVE DIED ***' on one of them; the next time the
user loads their page, they will see a different message. There is
nowhere to put this in MySingletonResource.
(Of course, the right way to do this is with a Mind object for HTTP,
but the browser will be unable to do any real updating unless it is
taking Donovan's psychoactive LivePage javascript. That particular
potent mix is unfortunately not yet suitable for beginners, but newcred
should be soon. Think of woven.guard as a 'gateway drug' to LivePage's
full flexibility.)
Please feel free to ask more questions, as I'd like to provide a
resource for doc-writers here on the list :)
More information about the Twisted-Python
mailing list