 |

 |
puzzlement | |
 |
 |
 |
 |
|
 |
 |
I would really have to work up to living alone, it's clear. Andrew's been
away this week and although it has hardly been a reprise of the great May 2009
ordering-pizza-so-I-wouldn't-have-to-leave-the-house-for-days disaster (I was
pretty sick, OK?) the last couple of days have been fairly bad.
Mum was here Monday and Tuesday nights and that was companionable and
pleasant. On Wednesday around the time she left it was clear to me that I was
starting to get a cold of some kind, which quickly degraded into me losing a
yoga mat while doing errands in the Macquarie Centre — not something people
find unamusing the next day, have you found a yoga mat in here? , and
also, no they hadn't — then heading to the implied yoga class and getting so
dizzy that my hearing dropped away. (Andrew tells me he's had that sensation
before, I tend to have vision rather than auditory problems when dizzy,
normally.)
The trouble with exercise is that clearly you can lay off it if you know
prior to doing it that you're sick, but you don't always know beforehand, and
if you don't you find out with considerable drama. I didn't want to tell the
teacher about it either, because this class is very very careful about my
pregnancy already in a way that I find a bit annoying to deal with. I know that
it's a very weird time for my abdominal muscles, yes, and lying on my stomach
is really uncomfortable, but please let me be the judge of my own upper arm
strength. The weight gain is only about 10% over my starting weight.
Last night I pushed out a major task that, I am not kidding, has been a
couple of years in the making, although I did spend most of that time intending
to get around to it rather than doing things. I had no idea how stressful the
immediate leadup had been frankly until I spent half of last night having my
old style semi-hallucinations about it. (Possibly I had a mild fever.) The
hallucinations are really a particularly annoying unrestful sort of semi-dream
state, where time passes extremely slowly and dream people argue with each
other incoherently. I used to have a night of that with every new job I got. It
tends to stop if I have my eyes open. Keeping them open is a problem.
By the time this morning came around, I was bloody tired, and utterly
socially incoherent. Was that a joke you made there? Or a meta-joke? Would it
be better if I played along, or if I got cranky? I'm especially bad, when
tired, at geek social jokes, which tend to revolve around identifying something
vaguely ambiguous in someone's phrasing, choosing the obviously wrong
interpretation, and making them stand by it.
So, that was 48 hours of my life I could have lived without. Janus has been
making himself a bit scarce: I wouldn't kick my housing either, if it was in
that kind of mood. He has stuck with small rearrangements.
It's not that I get sick more, I think, when Andrew is away, it's that a
small change in my energy levels makes it such a pain to care for myself. And
pregnancy plus illness is not really small, not in the first or third
trimesters.
I was going to take myself to Sculptures By the Sea tomorrow, but with my
increasingly unpleasant reaction to walking around and actual sickness I don't
think I can, sensibly at any rate. It's two trains and a bus in each direction,
and then a slow and hilly walk. (Everyone else in the world who owns a car is
thinking uh huh, that car-free caper is still ludicrous , but actually,
here's a little known pregnancy symptom for you: extreme motion sickness. I get
sick when I drive now. Mum said to ask the obstetricians about it,
and they suggested that not being pregnant would be a good solution.)
I need to get some sun though. Luckily, stone fruits are coming into season,
so I am motivated to head for the shops. And if I sleep well, perhaps to go
into the city and have Spice I Am with Andrew on his way home. Time for a
goodly bit of fire.
Originally posted at http://puzzling.org/logs/diary/2009/November/13/20091113 Tags: andrew, dreams, eating, illness, pregnancy, work, yoga
|
 |
 |
 |
 |
|
 |
 |


 |
jcalderone | |
 |
 |
 |
 |
|
 |
 |
Welcome to the 14th installment of "Twisted Web in 60 seconds". In many of the previous installments, I've demonstrated how to serve content by using existing resource classes or implementing new ones. In this installment, I'll demonstrate how you can use Twisted Web's basic or digest HTTP authentication to control access to these resources.
Guard, the Twisted Web module which provides most of the APIs which will be used in this example, helps you to add authentication and authorization to a resource hierarchy. It does this by providing a resource which implements getChild to return a dynamically selected resource. The selection is based on the authentication headers in the request. If those headers indicate the request is made on behalf of Alice, then Alice's resource will be returned. If they indicate it was made on behalf of Bob, his will be returned. If the headers contain invalid credentials, an error resource is returned. Whatever happens, once this resource is returned, URL traversal continues as normal from that resource.
The resource which implements this is HTTPAuthSessionWrapper, though it is directly is directly responsible for very little of the process. It will extract headers from the request and hand them off to a credentials factory to parse them according to the appropriate standards (eg HTTP Authentication: Basic and Digest Access Authentication) and then it hands the resulting credentials object off to a portal, the core of Twisted Cred, a system for uniform handling of authentication and authorization. I am not going to discuss Twisted Cred in much depth here. To make use of it with Twisted Web, the only thing you really need to know is how to implement a realm.
You need to implement a realm because the realm is the object which actually decides which resources are used for which users. This can be as complex or as simple as it suitable for your application. For this example, I'll keep it very simple: each user will have a resource which is a static file listing of the public_html directory in their UNIX home directory. First, I need to import implements from zope.interface and IRealm from twisted.cred.portal. Together these will let me mark this class as a realm (this is mostly - but notentirely - a documentation thing). I'll also need File for the actual implementation later.
from zope.interface import implements
from twisted.cred.portal import IRealm from twisted.web.static import File
class PublicHTMLRealm(object): implements(IRealm)
A realm only needs to implement one method, requestAvatar. This is called after any successful authentication attempt (ie, Alice supplied the right password). Its job is to return the avatar for the user who succeeded in authenticating. An avatar is just an object that represents a user. In this case, it will be a File. In general, with Guard, the avatar must be a resource of some sort.
def requestAvatar(self, avatarId, mind, *interfaces): if IResource in interfaces: return (IResource, File("/home/%s/public_html" % (avatarId,)), lambda: None) raise NotImplementedError()
A few notes on this method:
- The avatarId parameter is essentially the username. It's the job of some other code to extract the username from the request headers and make sure it gets passed here.
- The mind is always
None when writing a realm to be used with Guard. You can ignore it until you want to write a realm for something else.
- Guard always passed
IResource for the interfaces parameter. If interfaces only contains interfaces your code doesn't understand, raising NotImplementedError is the thing to do, as above. You'll only need to worry about getting a different interface when you write a realm for something other than Guard.
- If you want to track when a user logs out, that's what the last element of the returned tuple is for. It will be called when this avatar logs out.
lambda: None is the idiomatic no-op logout function.
- Notice that I have written the path handling code in this example very poorly. This example may be vulnerable to certain unintentional information disclosure attacks. This sort of problem is exactly the reason FilePath exists. However, that's an example for another day...
We're almost ready to set up the resource for this example. To create an HTTPAuthSessionWrapper, though, we need two things. First, a portal, which requires the realm above, plus at least one credentials checker:
from twisted.cred.portal import Portal from twisted.cred.checkers import FilePasswordDB
portal = Portal(PublicHTMLRealm(), [FilePasswordDB('httpd.password')])
FilePasswordDB is that credentials checker I mentioned. It knows how to read passwd(5)-style (loosely) files to check credentials against. It is responsible for the authentication work after HTTPAuthSessionWrapper extracts the credentials from the request.
Next we need either BasicCredentialFactory or DigestCredentialFactory. The former knows how to challenge HTTP clients to do basic authentication; the latter, digest authentication. I'll use digest here:
from twisted.web.guard import DigestCredentialFactory
credentialFactory = DigestCredentialFactory("md5", "example.org")
The two parameters to this constructor are the hash algorithm and the http authentication realm which will be used. The only other valid hash algorithm is "sha" (but be careful, MD5 is more widely supported than SHA). The http authentication realm is mostly just a string that is presented to the user to let them know why they're authenticating (you can read more about this in the RFC).
With those things created, we can finally instantiate HTTPAuthSessionWrapper:
from twisted.web.guard import HTTPAuthSessionWrapper
resource = HTTPAuthSessionWrapper(portal, [credentialFactory])
There's just one last thing that needs to be done here. When I introduced rpy scripts, I mentioned that they're evaluated in an unusual context. This is the first example which actually needs to take this into account. It so happens that DigestCredentialFactory instances are actually stateful. Authentication will only succeed if the same instance is used to generate challenges and examine the responses to those challenges. However, the normal mode of operation for an rpy script is for it to be re-executed for every request. This leads to a new DigestCredentialFactory being created for every request, preventing any authentication attempt from ever succeeding.
There are two ways to deal with this. First, the better of the two ways, I could move almost all of the code into a real Python module, including the code which instantiates the DigestCredentialFactory. This would make ensure the same instance was used for every request. Second, the easier of the two ways, I could add a call to cache to the beginning of the rpy script:
cache()
cache is part of the globals of any rpy script, so you don't need to import it (it's okay to be cringing at this point). Calling cache makes Twisted re-use the result of the first evaluation of the rpy script for subsequent requests too. Just what I want in this case.
Here's the complete example (with imports re-arranged to the more conventional style):
cache()
from zope.interface import implements
from twisted.cred.portal import IRealm, Portal from twisted.cred.checkers import FilePasswordDB from twisted.web.static import File from twisted.web.resource import IResource from twisted.web.guard import HTTPAuthSessionWrapper, DigestCredentialFactory
class PublicHTMLRealm(object): implements(IRealm)
def requestAvatar(self, avatarId, mind, *interfaces): if IResource in interfaces: return (IResource, File("/home/%s/public_html" % (avatarId,)), lambda: None) raise NotImplementedError()
portal = Portal(PublicHTMLRealm(), [FilePasswordDB('httpd.password')])
credentialFactory = DigestCredentialFactory("md5", "localhost:8080") resource = HTTPAuthSessionWrapper(portal, [credentialFactory])
And voila, a password-protected per-user Twisted Web server.
I've gotten several requests to write something about sessions, so there's a good chance that's what you'll find in the next installment.
Tags: cred, documentation, http auth, python, sixty seconds, twisted, web
|
 |
 |
 |
 |
|
 |
 |

 |
jcalderone | |
 |
 |
 |
 |
|
 |
 |
As of Monday, the 9th, I will be considering opportunities for short term consulting and contract work. Please feel free to contact me if you have a software challenge to tackle, particularly if it involves one or more of Python, Twisted, networking, event-driven architectures, massive scaling, or open source software.
Immediately following the demise of Divmod this summer, I took a job at a major international corporation. A number of factors conspired to make this decision non-viable in the long term. Today I gave notice. I'm excited to be able to get back to doing what I love - solving challenging, interesting problems in a flexible, open environment.
One of the other things I've been unable to do since even before Divmod's end is commit serious time to Twisted development and maintenance. This is something else I'm looking forward to re-engaging in. I made a sizable dent in Twisted's open ticket count last fall and winter, thanks to funding from the Twisted Software Foundation (in turn thanks to all of the Twisted founding sponsors). I'll be able to continue this work thanks to this year's sponsors (visible on the front page of the Twisted site), though perhaps not to the same extent. If you'd like to help out in this regard, become a sponsor! All donations are useful and appreciated!
Tags: python, twisted
|
 |
 |
 |
 |
|
 |
 |

 |
puzzlement | |
 |
 |
 |
 |
|
 |
 |
I've often wondered about why going from a 25℃ day to a 35℃ day is a kind of
death, but 35℃ to 45℃ (well, more usually 41℃ or so, I've only ever experienced
one day above 45℃, even if it does count for extra spending that morning in
direct sunlight clad in a 5mm scuba wetsuit that I bought for use in winter) is
a difference in kind only. I gather most of our extremely unseasonable days
come in from the desert though, so I suppose the difference is humidity.
Mid-summer in Sydney can turn on 38℃ and humid in season, but the desert will
send 38℃ and dry out of it.
Today was a desert day, forecast 35℃, actual temperatures closer to 39℃, but
dry and windy. It was not unsurvivable, and I say this from the perspective of
third trimester, usually proclaimed as a lifetime peak of heat intolerance. But
I am yet to be tested against humidity and heat together.
I'm going to watch the forecasts though, and carry a water bottle around for
hot days, because a train got stuck on the Harbour Bridge for 40 minutes today
and 40 minutes in unairconditioned railway carriages full of angry people will
make me pretty ill now, but without water I'd be in big trouble.
The other great thing about desert days is that they're always broken with
southerlies. Well, great for me because I can sit around and enjoy the small
adrenaline rush that comes with having the weather try and break into my house.
Less great for anyone fighting the fires that start on desert days, although it
doesn't seem that today was too bad in that respect.
We beached on Sunday. My upbringing has misled me: my family were never at
the beach between 11am and 3:30pm. You wake up with the beach. You say good
afternoon with the beach. You don't stay there for the peak of the UV index. So
I'm always mildly surprised to be sitting in the sun with cheerful pale skinned
people at noon. As best I can tell the surf is always pretty substantial at
Bronte, and I don't trust myself with my new planetary shape in the surf, so I
stuck to the giant rock pools and the ocean bath. Being in the water was
astoundingly pleasant; being out of it must be getting more uncomfortable than
I had realised.
Andrew said that the surf was good quality, but it was typical Sydney: the
flags for safe swimming were all of about five metres apart, and there was a
rip down the middle of the beach twice as wide as that (Pete was annoyed, the
bits without waves always look more appealing). There must have been a decent
current too, someone got swept right down the beach to the south. Turns out if
you shout loudly enough, essentially all of Bronte will hear you. (A lifesaver
went out efficiently on a big blue longboard well before he was at serious risk
of being tossed onto the rocks.)
It feels like I should slow my life down, and generally this year
has not been as hectic as last, one upside of many of my friends moving away I
suppose. But for example this weekend, on top of the beach on Sunday we had an
infant care class on Saturday (lots of videos of babies grizzling) and then
went to Jamie and Lisa's Halloween/birthday party. We decided to rent a car for
the Saturday extravaganza. I can see why people with small cars drive
everywhere; if you regard the purchase and upkeep of the car as a sunk cost,
the fuel makes it considerably cheaper than public transport. We spent $5.77 on
fuel for the day. The downside was parking in Pyrmont. Most of the parking is 1
or 2 hours, 24 hours a day, due to the nearby casino. We eventually wedged our
teeny rental Getz in half a car spot.
I was pretty lazy about my costume this year. Partly it's just hard to top
Andrew's effort in cutting off more than a foot of hair for a party in 2006.
But I'm also trying to limit the number of outfits I buy that are designed to
fit me and my 40cm passenger. If I could have found a cheap way to go as Saturn
I would have. (For bonus points, Janus is a moon of Saturn.) Anyway, now Janus
has some pirate gear for his dress-up box.
Originally posted at http://puzzling.org/logs/diary/2009/November/3/20091103 Tags: beach, driving, pregnancy, socialising, summer, weather
|
 |
 |
 |
 |
|
 |
 |

|
 |
|
 |