Tuesday, July 12. 2011
It took me a long time to figure this out, I didn’t want it to end up locked in my head, so here’s a rough guide on how I managed to get a pylons 0.9.7 project to do LDAP authentication. All of the below is done in config/middleware.py.
The key points are:
- “import ldap” (for the obvious reason) and “from paste.auth.basic import AuthBasicHandler”
- Wrap the stacked WSGI ‘app’ object in the AuthBasicHandler you just imported:
app = AuthBasicHandler(app, 'The value you want to appear on the browser dialog box', yourAuthFunctionHere)
- Define an auth function “yourAuthFunctionHere(requestEnvironment, username, password)”. Some tricky bits:
- If this function returns “True”, the request will proceed and there will be a new key in the Request object named ‘REMOTE_USER’ that contains the passed username.
- If this function returns “False”, the user will be prompted again until it succeeds. (I should probably find a way to limit these so people cannot just keep guessing.)
- Python’s LDAP has some strange behaviours:
- Do ldap.initialize outside the auth function or you will swamp the LDAP server with bind requests
- Use synchronous bind (‘bind_s’ as opposed to ‘bind’) – asynchronous bind in the WSGI middleware layer caused some really bizarre behaviour here, including segfaults
- Trap ldap.INVALID_CREDENTIALS and return False when it is raised, or watch all invalid logins crash the server process
- bind (and bind_s) return a tuple, if the first item in the tuple is int(97), the bind worked – otherwise, they might have bound but as an anonymous user, and we need to fail
- the second item in that tuple is a list of messages from the server – I’m not handling those at all today, becuase in the sole case we’re interested in (97, or “auth”), the messages appear to be blank in our environment – there’s no reason to believe this is consistent, and I should probably research this further at some point to provide meaningful feedback to the user on failed auth.
You can’t touch the session from within the WSGI middleware layer
All of this was done because a new server in our datacenter doesn’t have packages for the old build of Apache that we used to configure our LDAP auth back in the day. I’m actually quite pleased at how the new system works, and am glad to be rid of that Apache+PHP millstone that’s been lurking as a dependancy in all of our Pylons projects simply for the LDAP authentication solution.
Tuesday, October 5. 2010
I have a project at work where we are using Pylons/paster as the web service provider. One of the “clients” of this service is written in Flex/Flash, and had as a component the AlivePDF AS3 library for generating a static PDF of the Flash content.
Unfortunately, because Flash cannot save content locally, in order to actually GET this PDF content back to the user, AlivePDF posts a byte array to the service and expects the service to bundle that as a PDF and send it back. They provide a “content.php” file as an example – which, frankly, is some pretty inscrutable code until you manage to figure out what it’s working around. So when one of my employees (the one who selected AlivePDF in the first place) sent me the PHP file, it took as long to understand what he wanted as it did to come up with a Pylons solution. You can find the latter bit below – replace ${service} and ${controller} with your actual values through-out, and trim the ellipses (they are there to indicate there may be additional context on either side of the line I’m giving you):
${service}/config/routing.py:
def make_map():
…
map.connect(’/${controller}/create.php’, controller=’${controller}’, action=‘pdf’) # put this before the default routes, if you have any
…
${service}/controllers/${controller}.py:
Class ${controller}(BaseController):
…
def pdf(self):
response.headers[‘Content-Type’] = request.environ[‘CONTENT_TYPE’]
response.headers[‘Content-Disposition’] = ‘inline; filename=”%s”’ % request.GET[‘name’]
return request.environ[‘wsgi.input’].read(int(request.environ[‘CONTENT_LENGTH’]))
…
Tuesday, October 14. 2008
As a Code Kata, I’ve been working my way through the Project Euler stuff.
Here’s the problem.
Here’s my solution:
[highlight lang=py]
- A generator to derive the Fibonacci sequence
def fib():
x, y = 0, 1
yield x
yield y
while 1:
x, y = y, x+y
yield y
if name == ‘main’:
idx = 0
sum = 0
gen = fib()
while idx <= 4000000:
idx = gen.next()
if (g % 2 == 0): h = h+g
print h
[/highlight]
Note: You want to be careful how you use infinite generators like the ‘fib()’ I created above.
One guy on the forums noted:
Now, replacing an odd number with O and an even with E, we get: O, O, E, O, O, E, O, O, E, O, O, E, O, O, E…And so each third number is even. We don’t need to calculate the odd numbers. Starting from an two odd terms x, y, the series is: x, y, x + y, x + 2y, 2x + 3y, 3x + 5y
Thursday, October 2. 2008
Spent the day poking at Pylons 0.9.7rc2 as well as the new documentation site that came out with the release of Python 2.6 – some sweet stuff in there.
WebOb: The New Middleware Layer on 0.9.7, its Reference page, How WebOb is different and Ian Bicking’s Blog Post about it – Ian is one of those guys who is so smart it fills me with a jealous rage. I’ve already found a lot of useful stuff in WebOb that was missing or hard to do with the older Pylons middleware.
Base Pylons Documentation, AuthKit Cookbook, Roadmap to Pylons 1.0 – just some good links to pylonshq
Python modules I need to brush up on, or just links I used in the course of today’s work:
2.6 Documentation Index, shutil, xdrlib, functools – a lot of functional paradigm stuff moved here?, StringIO, tempfile – probably the best tempfile implementation by a programming language, ever, simpleJSON revved to 2.0.1, 2.6 ‘Standard Library’ Index
Mako – I’m not sure why there are so dang many Pythons template layers. In fact, Preppy is another one that I might use more in the future due to its integration with ReportLab’s PDF Toolkit
The Pylons Book – I’ve already pre-purchased, but wish it had more “nuts and bolts” based on what I’ve seen so far.
|