From 9f48612a56b6e760aa06a9af2071f1b50f413f27 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Thu, 22 Jan 2009 14:00:37 -0700 Subject: Sundry work getting ready to switch to new XML-RPC client/server code --- ipalib/plugins/xmlclient.py | 29 +++++++++++++++++++++++ ipalib/rpc.py | 53 +++++++++++++++++++++++++++++++++++++++++- ipaserver/plugins/xmlserver.py | 29 +++++++++++++++++++++++ tests/test_ipalib/test_rpc.py | 16 ++++++++++++- tests/util.py | 7 ++++++ 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 ipalib/plugins/xmlclient.py create mode 100644 ipaserver/plugins/xmlserver.py diff --git a/ipalib/plugins/xmlclient.py b/ipalib/plugins/xmlclient.py new file mode 100644 index 000000000..fa5afb553 --- /dev/null +++ b/ipalib/plugins/xmlclient.py @@ -0,0 +1,29 @@ +# Authors: +# Jason Gerard DeRose +# Rob Crittenden +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +XML-RPC client plugin. +""" + +from ipalib import api + +if 'in_server' in api.env and api.env.in_server is False: + from ipalib.rpc import xmlclient + api.register(xmlclient) diff --git a/ipalib/rpc.py b/ipalib/rpc.py index e7823ef95..e845b8939 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -1,5 +1,6 @@ # Authors: # Jason Gerard DeRose +# Rob Crittenden # # Copyright (C) 2008 Red Hat # see file 'COPYING' for use and warranty information @@ -31,7 +32,8 @@ Also see the `ipaserver.rpcserver` module. from types import NoneType import threading -from xmlrpclib import Binary, Fault, dumps, loads +from xmlrpclib import Binary, Fault, dumps, loads, ServerProxy, SafeTransport +import kerberos from ipalib.backend import Backend from ipalib.errors2 import public_errors, PublicError, UnknownError from ipalib.request import context @@ -161,15 +163,62 @@ def xml_loads(data): return (xml_unwrap(params), method) +class KerbTransport(SafeTransport): + """ + Handles Kerberos Negotiation authentication to an XML-RPC server. + """ + + def get_host_info(self, host): + + (host, extra_headers, x509) = SafeTransport.get_host_info(self, host) + + # Set the remote host principal + service = "HTTP@" + host.split(':')[0] + + try: + (rc, vc) = kerberos.authGSSClientInit(service) + except kerberos.GSSError, e: + raise e # FIXME: raise a PublicError + + try: + kerberos.authGSSClientStep(vc, "") + except kerberos.GSSError, e: + raise e # FIXME: raise a PublicError + + extra_headers += [ + ('Authorization', 'negotiate %s' % kerberos.authGSSClientResponse(vc)) + ] + + return (host, extra_headers, x509) + + class xmlclient(Backend): """ Forwarding backend for XML-RPC client. """ + connection_name = 'xmlconn' + def __init__(self): super(xmlclient, self).__init__() self.__errors = dict((e.errno, e) for e in public_errors) + def connect(self, ccache=None, user=None, password=None): + if hasattr(context, self.connection_name): + raise StandardError( + '%s.connect(): context.%s already exists in thread %r' % ( + self.name, self.connection_name, threading.currentThread().getName() + ) + ) + conn = ServerProxy(self.env.xmlrpc_uri, + transport=KerbTransport(), + allow_none=True, + ) + setattr(context, self.connection_name, conn) + + def get_connection(self): + return getattr(context, self.connection_name) + def forward(self, name, *args, **kw): """ Forward call to command named ``name`` over XML-RPC. @@ -197,6 +246,8 @@ class xmlclient(Backend): response = command(xml_wrap(params)) return xml_unwrap(response) except Fault, e: + self.debug('Caught fault %d from server %s: %s', e.faultCode, + self.env.xmlrpc_uri, e.faultString) if e.faultCode in self.__errors: error = self.__errors[e.faultCode] raise error(message=e.faultString) diff --git a/ipaserver/plugins/xmlserver.py b/ipaserver/plugins/xmlserver.py new file mode 100644 index 000000000..51b990ffb --- /dev/null +++ b/ipaserver/plugins/xmlserver.py @@ -0,0 +1,29 @@ +# Authors: +# Jason Gerard DeRose +# Rob Crittenden +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +XML-RPC client plugin. +""" + +from ipalib import api + +if 'in_server' in api.env and api.env.in_server is True: + from ipaserver.rpcserver import xmlserver + api.register(xmlserver) diff --git a/tests/test_ipalib/test_rpc.py b/tests/test_ipalib/test_rpc.py index bc8936ab6..30175e3bf 100644 --- a/tests/test_ipalib/test_rpc.py +++ b/tests/test_ipalib/test_rpc.py @@ -22,7 +22,7 @@ Test the `ipalib.rpc` module. """ import threading -from xmlrpclib import Binary, Fault, dumps, loads +from xmlrpclib import Binary, Fault, dumps, loads, ServerProxy from tests.util import raises, assert_equal, PluginTester, DummyClass from tests.data import binary_bytes, utf8_bytes, unicode_str from ipalib.frontend import Command @@ -184,6 +184,20 @@ class test_xmlclient(PluginTester): """ _plugin = rpc.xmlclient + def test_connect(self): + (o, api, home) = self.instance('Backend', in_server=False) + + # Test that StandardError is raised if conntext.xmlconn already exists: + context.xmlconn = 'The xmlrpclib.ServerProxy instance' + e = raises(StandardError, o.connect) + assert str(e) == '%s.connect(): context.%s already exists in thread %r' % ( + 'xmlclient', 'xmlconn', threading.currentThread().getName() + ) + + del context.xmlconn + o.connect() + assert isinstance(context.xmlconn, ServerProxy) + def test_forward(self): """ Test the `ipalib.rpc.xmlclient.forward` method. diff --git a/tests/util.py b/tests/util.py index f5899dfab..631d4a05c 100644 --- a/tests/util.py +++ b/tests/util.py @@ -295,6 +295,13 @@ class PluginTester(object): o = api[namespace][self.plugin.__name__] return (o, api, home) + def tearDown(self): + """ + nose tear-down fixture. + """ + for name in context.__dict__.keys(): + delattr(context, name) + class dummy_ugettext(object): __called = False -- cgit