These classes subclass several of the culprits in twisted.web.xmlrpc that are responsible for not being able to make autenticated XML-RPC calls.
Note that this recipe also makes use of the URI classes that were posted in a previous recipe here: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473864
I will submit a patch to twisted along these lines, but there will be no references to the custom Uri module or code.
Update: XML-RPC Authentication is now supported in Twisted (version 2.4 included a patch based on this recipe). If you are using a recent version of Twisted or can update to a recent version, this recipe is no longer needed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
import base64 import xmlrpclib from twisted.web import xmlrpc from twisted.internet import reactor, defer from where.you.installed.uri import Uri class AuthQueryProtocol(xmlrpc.QueryProtocol): ''' We're just over-riding the connectionMade() method so that we can add the Authorization header. ''' def connectionMade(self): self.sendCommand('POST', self.factory.url) self.sendHeader('User-Agent', 'Twisted/XMLRPClib') self.sendHeader('Host', self.factory.host) self.sendHeader('Content-type', 'text/xml') self.sendHeader('Content-length', str(len(self.factory.payload))) if self.factory.user: auth = base64.encodestring('%s:%s' % ( self.factory.user, self.factory.password)) self.sendHeader('Authorization', 'Basic %s' % auth) self.endHeaders() self.transport.write(self.factory.payload) class AuthQueryFactory(xmlrpc.QueryFactory): ''' We're using a Uri object here for the url, diverging pretty strongly from how it's done in t.w.xmlrpc. This is done for convenience and simplicity of presentation in this recipe. ''' deferred = None protocol = AuthQueryProtocol def __init__(self, url, method, *args): self.url, self.host = url.path, url.host self.user, self.password = url.user, url.password self.payload = xmlrpc.payloadTemplate % ( method, xmlrpclib.dumps(args)) self.deferred = defer.Deferred() class AuthProxy: ''' A Proxy for making remote XML-RPC calls that supports Basic Authentication. There's no sense subclassing this, since it needs to override all of xmlrpc.Proxy. Pass the URL of the remote XML-RPC server to the constructor. Use proxy.callRemote('foobar', *args) to call remote method 'foobar' with *args. ''' def __init__(self, url): self.url = Uri(url) self.host = self.url.host self.port = self.url.port self.secure = self.url.scheme == 'https' def callRemote(self, method, *args): factory = AuthQueryFactory(self.url, method, *args) if self.secure: from twisted.internet import ssl reactor.connectSSL(self.host, self.port or 443, factory, ssl.ClientContextFactory()) else: reactor.connectTCP(self.host, self.port or 80, factory) return factory.deferred def _test(): ''' In this test, we'll hit a custom Zope/Plone script on a local Zope server. The output will give us this: [('authenticated', True), ('name', 'admin'), ('roles', ['Manager', 'Authenticated']), ('id', 'admin')] ''' def callback(data): print data.items() reactor.stop() def errback(failure): #print failure.getErrorMessage() reactor.stop() server = AuthProxy('http://admin:email@example.com:9673/site01/') d = server.callRemote('getXMLRPCUserInfo') d.addCallback(callback) d.addErrback(callback) reactor.run() if __name__ == '__main__': _test()
Use it like it shows in _test() ;-)
Twisted xmlrpc auth. Hi I am looking for this in Twisted 2.5 and can't seem to find it. Twisted is a large package so perhaps I am just not finding it. Can you advise the location of the code in twisted. Many thanks.
rename QueryFactory to _QueryFactory. Nice Recipe thanks. In Twisted 2.5.0 it seems that QueryFactory is not in twisted.web.xmlrpc, but _QueryFactory is. Renaming it in your script fixes it.
Thanks for the recipe.
and now I see why. Never mind, ignore that post.