[Twisted-Python] New Guard question

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. Looking at the webhappyrealm.py example in the sandbox, it's not very clear what the returned resources represent. Also, returning Resources seems a little weird. To me, a resource is whole page, why would I want a whole page returned? cheers, jml

On Thursday, June 19, 2003, at 02:33 PM, jml@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 :)

On Sat, Jun 21, 2003 at 01:05:47AM -0500, Glyph Lefkowitz wrote:
On Thursday, June 19, 2003, at 02:33 PM, jml@ids.org.au wrote:
What does the returned Resource represent?
[confuso-ray generating padding] The whole UI for that user. Send more questions.
I assume this is a correct summary. So, I guess this means that for a web application with that allows anonymous users to do and see many things on many pages, there'll be many Portal and Realm instances? Or is this where the mysterious mind comes into play? Also, your badidea example didn't really help me. Firstly, I don't use weird guard sessions, I just use normal sessions. Secondly, much of the if/then logic is wrapped in Views that provide patterns to the templates. Example: <div view="authProtected"> <span pattern="anonymous"><a href="login">Login</a></span> <span pattern="loggedIn"><a href="logout">Logout</a> (<a view="personLink"/>)</span> </div> It's hard to see how to do this with guard. This email is probably premature, as I'm still fiddling around with some trivial examples trying to get stuff to work. (Examples using Page.wchild_* and getDynamicChild methods, rather than resource.putChild) cheers, jml

On Monday, June 23, 2003, at 12:02 PM, Jonathan Lange wrote:
So, I guess this means that for a web application with that allows anonymous users to do and see many things on many pages, there'll be many Portal and Realm instances? Or is this where the mysterious mind comes into play?
Assuming you mean a single application, there will be one Portal to access the site, one Realm to dish out resources, and then each resource will return a tree. But, of course, "application" is a funny word; if you are making a site that integrates lots of different pre-existing components there may be multiple Realms, arranged in some sort of hierarchy with rules for how and when one dispatches to another. (For example, the top-level Realm, attached to the Portal, may specify that a certain child URL maps to another Realm's IResource children, and that the user is preauthenticated ... this will probably need some infrastructure work.)
Also, your badidea example didn't really help me. Firstly, I don't use weird guard sessions, I just use normal sessions.
"normal" sessions are kinda broken; you should probably update your code to use guarded sessions. In fact, I sure would like to deprecate the old session code (for reasons related to browser eccentricities that are out of scope for this discussion) but I will probably never be able to because it's so widely used at this point.
Secondly, much of the if/then logic is wrapped in Views that provide patterns to the templates. Example:
<div view="authProtected"> <span pattern="anonymous"><a href="login">Login</a></span> <span pattern="loggedIn"><a href="logout">Logout</a> (<a view="personLink"/>)</span> </div>
It's hard to see how to do this with guard.
Your Realm will return a View which wraps a Model. The anonymous model is different from the logged-in one, but the top-level Views will likely be of the same class. You might want to write a generic 'switch' view that looks something like this: <div view="switch" attribute="hasUser"> <span case="1"><a href="logout">Logout</a></span> <span case="0"><a href="login">Login</a></span> </div> If this is not obvious - the idea is that you specify a submodel (or perhaps some other interface which the View can talk to) "hasUser". The 'anonymous' model will say "0" and the logged-in-user model will say "1". This kind of functionality is also useful to specify 'template adapters' in your template code, to specify different display styles in-line in a list for different model classes. I've done this in ad-hoc ways once or twice; I should probably check in a "real" version to woven at some point.
This email is probably premature, as I'm still fiddling around with some trivial examples trying to get stuff to work. (Examples using Page.wchild_* and getDynamicChild methods, rather than resource.putChild)
Nevertheless, I don't have the time to write coherent documentation for this stuff right now, so hopefully these emails are generating fodder for someone who can :).
participants (3)
-
Glyph Lefkowitz
-
jml@ids.org.au
-
Jonathan Lange