[Twisted-Python] How to do basic authentication on twisted web
Hi there, I'm new to twisted, so please excuse the noobish question, but: I'm using twisted-web on an openwrt computer, using busybox, and after looking through the docs, I'm a little unclear about how to implement something like apache's htpasswd style authentication. Using apache, I'd do something like: htpasswd -c passwrods admin topSecretPassword What's the simplest way to add a simple check for a username and password like this using twisted web? Where possible, I'd like to keep all of this inside twisted rather than rely on installing other binaries, that might not work within the confined of a small openwrt install. C -- --- I'm currently only checking my email at 9am, midday and at 4pm. If you need a response from me urgently, please call or text my mobile, or contact me via Skype (chris.d.adams). --- Chris Adams Stemcel Studios The Hub 5 Torrens Street London EC1V 1NQ email: chris@stemcel.co.uk web: www.stemcel.co.uk twitter:chris_d_adams skype: chris.d.adams mob: 07974 368 229 tel: 0207 558 8971
Hi Chris,
I'm using twisted-web on an openwrt computer, using busybox, and after looking through the docs, I'm a little unclear about how to implement something like apache's htpasswd style authentication.
Using apache, I'd do something like:
htpasswd -c passwrods admin topSecretPassword
What's the simplest way to add a simple check for a username and password like this using twisted web?
Where possible, I'd like to keep all of this inside twisted rather than rely on installing other binaries, that might not work within the confined of a small openwrt install.
The short answer is that you need to use twisted.cred. It's an abstraction that allows you to implement authentication without directly exposing the hashing method or password database details. Take a look at this example: http://twistedmatrix.com/projects/web/documentation/examples/webguard.py It demonstrates an in memory database, which should generally be used for testing/debugging only - but you might not care about that an be happy to have an in memory database. If you want to use an .htaccess apache style password db, you'll have to make a slight modification: from twisted.web import guard, server from twisted.cred.portal import Portal from twisted.cred.checkers import FilePasswordDB import crypt def cmp_pass(uname, password, storedpass): return crypt.crypt(password, storedpass[:2]) checkers = [FilePasswordDB(path_to_htpasswd, hash=cmp_pass)] wrapper = guard.HTTPAuthSessionWrapper( Portal(SimpleRealm(), checkers), [guard.BasicCredentialFactory('yoursite.com')]) return internet.TCPServer(8080, server.Site(resource=wrapper)) I'll probably contribute the Htaccess style checker to twisted at some point, as I've found it useful in the past and others may too. Cheers, Reza -- Reza Lotun +44 (0)7521 310 763 rlotun@gmail.com
On Mon, Aug 17, 2009 at 11:45 AM, Reza Lotun <rlotun@gmail.com> wrote:
The short answer is that you need to use twisted.cred.
You don't need to do that at all, it just happens to be the best way.
wrapper = guard.HTTPAuthSessionWrapper( Portal(SimpleRealm(), checkers), [guard.BasicCredentialFactory('yoursite.com')])
Where is Portal and SimpleRealm in your example? Those are actually quite large constructs which are crucial to guard doing anything useful at all. Since there are numerous circumstances that people need Htauth, it is implemented as follows from twisted.web import http # and other stuff... class HTTPAuthPage(rend.Page): def renderHTTP(self, ctx): request = inevow.IRequest(ctx) username, password = request.getUser(), request.getPassword() if [My auth details check out]: return rend.Page.renderHTTP(self, ctx) else: request.setHeader('WWW-Authenticate', 'Basic realm="My realm name"') request.setResponseCode(http.UNAUTHORIZED) return "Authentication required."
Hi Colin,
Where is Portal and SimpleRealm in your example? Those are actually quite large constructs which are crucial to guard doing anything useful at all.
Sorry, this snippet of code I wrote tracked the webguard.py example that I linked to. from twisted.cred.portal import IRealm, Portal class SimpleRealm(object): """ A realm which gives out L{GuardedResource} instances for authenticated users. """ implements(IRealm) def requestAvatar(self, avatarId, mind, *interfaces): if resource.IResource in interfaces: return resource.IResource, GuardedResource(), lambda: None raise NotImplementedError() Does that make sense now?
Since there are numerous circumstances that people need Htauth, it is implemented as follows
Ok, of course you can do that if you want. But you still need to be able to *verify* credentials. Anyway, to each their own. Reza -- Reza Lotun mobile: +44 (0)7521 310 763 email: rlotun@gmail.com work: reza@tweetdeck.com twitter: @rlotun
Wow, thanks fo the quick replies guys. I've tried to combine this with the example earlier, with some annotations as to what I think is going on, but I'm on somewhat shaky ground here. Do these annotations sum up roughly what is going on here? My annotated example is also up on github - http://gist.github.com/169102 The original on twisted matrix http://twistedmatrix.com/projects/web/documentation/examples/webguard.py 2009/8/17 Reza Lotun <rlotun@gmail.com>:
Hi Colin,
Where is Portal and SimpleRealm in your example? Those are actually quite large constructs which are crucial to guard doing anything useful at all.
Sorry, this snippet of code I wrote tracked the webguard.py example that I linked to.
from twisted.cred.portal import IRealm, Portal
class SimpleRealm(object): """ A realm which gives out L{GuardedResource} instances for authenticated users. """ implements(IRealm)
def requestAvatar(self, avatarId, mind, *interfaces): if resource.IResource in interfaces: return resource.IResource, GuardedResource(), lambda: None raise NotImplementedError()
Does that make sense now?
Since there are numerous circumstances that people need Htauth, it is implemented as follows
Ok, of course you can do that if you want. But you still need to be able to *verify* credentials.
Anyway, to each their own.
Reza
-- Reza Lotun mobile: +44 (0)7521 310 763 email: rlotun@gmail.com work: reza@tweetdeck.com twitter: @rlotun
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
-- --- I'm currently only checking my email at 9am, midday and at 4pm. If you need a response from me urgently, please call or text my mobile, or contact me via Skype (chris.d.adams). --- Chris Adams Stemcel Studios The Hub 5 Torrens Street London EC1V 1NQ email: chris@stemcel.co.uk web: www.stemcel.co.uk twitter:chris_d_adams skype: chris.d.adams mob: 07974 368 229 tel: 0207 558 8971
Hi Chris,
My annotated example is also up on github - http://gist.github.com/169102
The original on twisted matrix http://twistedmatrix.com/projects/web/documentation/examples/webguard.py
Generally it looks fine - but I should mention these random snippets of code were given with the assumption that a .tac file is being used. A .tac file is basically a "runner" script or driver for your twisted program - it works in conjunction with the twistd command-line utility and does fun stuff like automatic daemonization and reactor selection. Anyway, the snippets I gave would integrate into the webguard.py example like so (imports omitted for clarity): class GuardedResource(resource.Resource): """ A resource which is protected by guard and requires authentication in order to access. """ def getChild(self, path, request): return self def render(self, request): return "Authorized!" class SimpleRealm(object): """ A realm which gives out L{GuardedResource} instances for authenticated users. """ implements(IRealm) def requestAvatar(self, avatarId, mind, *interfaces): if resource.IResource in interfaces: return resource.IResource, GuardedResource(), lambda: None raise NotImplementedError() def cmp_pass(uname, password, storedpass): return crypt.crypt(password, storedpass[:2]) def main(): log.startLogging(sys.stdout) # add in the path to your .htpasswd in place of path_to_htpasswd checkers = [FilePasswordDB(path_to_htpasswd, hash=cmp_pass)] wrapper = guard.HTTPAuthSessionWrapper( Portal(SimpleRealm(), checkers), [guard.BasicCredentialFactory('yoursite.com')]) reactor.listenTCP(8080, server.Site( resource = wrapper)) reactor.run() if __name__ == '__main__': main() This should basically run (though I haven't tested this specific example). Cheers, Reza -- Reza Lotun mobile: +44 (0)7521 310 763 email: rlotun@gmail.com work: reza@tweetdeck.com twitter: @rlotun
Thanks Reza,
Generally it looks fine - but I should mention these random snippets of code were given with the assumption that a .tac file is being used. A .tac file is basically a "runner" script or driver for your twisted program - it works in conjunction with the twistd command-line utility and does fun stuff like automatic daemonization and reactor selection.
Automatic daemonization and monitoring? This sounds like exactly what I'm after. I've been calling twisted like so for a project: explicitly setting the process id, logger and source file here. And relying on a separate cron script to check if all is well: twistd --pidfile=$PIDFILE --syslog --prefix=program_name --python program_name.py Would using a .tac file make some of these flags redundant? All I could found about .tac files is here - http://twistedmatrix.com/projects/core/documentation/howto/application.html Are there any other resources where I can found out about the advantages of using .tac files instead the way I've bee doing stuff? -- --- I'm currently only checking my email at 9am, midday and at 4pm. If you need a response from me urgently, please call or text my mobile, or contact me via Skype (chris.d.adams). --- Chris Adams Stemcel Studios The Hub 5 Torrens Street London EC1V 1NQ email: chris@stemcel.co.uk web: www.stemcel.co.uk twitter:chris_d_adams skype: chris.d.adams mob: 07974 368 229 tel: 0207 558 8971
Hi Chris,
Automatic daemonization and monitoring? This sounds like exactly what I'm after.
I've been calling twisted like so for a project: explicitly setting the process id, logger and source file here. And relying on a separate cron script to check if all is well:
twistd --pidfile=$PIDFILE --syslog --prefix=program_name --python program_name.py
Would using a .tac file make some of these flags redundant?
All I could found about .tac files is here - http://twistedmatrix.com/projects/core/documentation/howto/application.html
Are there any other resources where I can found out about the advantages of using .tac files instead the way I've bee doing stuff?
Well, it won't make all the flags redundant - the default .pid file is still twistd.pid, and you'd have to set your --prefix, etc. However, you definitely should be using .tac files since you can handle daemonization through twistd by using the application framework (you can select a reactor from twistd by using the -r flag - for example `twistd -r epoll -y myapp.tac --pidfile=...`). The link you've posted is the de-facto source for .tac file documentation. I can post a simple .tac file used for an internal web tool: --- from twisted.application import service, internet from twisted.internet import defer from twisted.python.log import ILogObserver, FileLogObserver from twisted.python.logfile import DailyLogFile from twisted.web import guard, server from twisted.cred.portal import Portal from twisted.cred.checkers import FilePasswordDB from tdusers.lookup import SimpleRealm import crypt def cmp_pass(uname, password, storedpass): return crypt.crypt(password, storedpass[:2]) def get_api_service(): """ Return a service suitable for creating an application object. """ checkers = [FilePasswordDB('/home/repo/.htpasswd', hash=cmp_pass)] wrapper = guard.HTTPAuthSessionWrapper( Portal(SimpleRealm(), checkers), [guard.BasicCredentialFactory('auth')]) return internet.TCPServer(8080, server.Site(resource=wrapper)) application = service.Application('Lookup') logfile = DailyLogFile('lookup.log', '.') application.setComponent(ILogObserver, FileLogObserver(logfile).emit) # attach the service to its parent application service = get_api_service() service.setServiceParent(application) ---- I then start my tool using twistd -r epoll -y mytacfile.tac. The good thing about this approach is that your code can live in a seperate file and no mention of the *running* of it has to be made in that file - that is, you shouldn't have to explicity choose what reactor you're using since that's more of a deployment question. I don't really know of a more comprehensive source for twistd and .tac files - I suppose if you fancy dong a writeup that'd be appreciated by the rest of us ;-) Cheers, Reza -- Reza Lotun mobile: +44 (0)7521 310 763 email: rlotun@gmail.com work: reza@tweetdeck.com twitter: @rlotun
participants (3)
-
Chris Adams
-
Colin Alston
-
Reza Lotun