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:admin@192.168.4.10: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.