summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipa_server/rpcserver.py32
-rw-r--r--ipalib/plugins/b_xmlrpc.py3
-rw-r--r--ipalib/rpc.py86
-rw-r--r--tests/test_ipalib/test_rpc.py123
4 files changed, 242 insertions, 2 deletions
diff --git a/ipa_server/rpcserver.py b/ipa_server/rpcserver.py
new file mode 100644
index 000000000..1fbbf4aa4
--- /dev/null
+++ b/ipa_server/rpcserver.py
@@ -0,0 +1,32 @@
+# Authors:
+# Jason Gerard DeRose <jderose@redhat.com>
+#
+# 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
+
+"""
+Dispatcher for RPC server.
+"""
+
+from ipalib import Backend
+from ipalib.rpc import xmlrpc_wrap, xmlrpc_unwrap
+
+
+class XMLServer(Backend):
+
+ def dispatch(self, method, params):
+ self.info('Received call to %r', method)
+ params = xml_unwrap(params)
diff --git a/ipalib/plugins/b_xmlrpc.py b/ipalib/plugins/b_xmlrpc.py
index b6e113a5c..14f2a9bed 100644
--- a/ipalib/plugins/b_xmlrpc.py
+++ b/ipalib/plugins/b_xmlrpc.py
@@ -47,11 +47,10 @@ class xmlrpc(Backend):
# Are there any reasonably common XML-RPC client implementations
# that don't support the <nil/> extension?
# See: http://docs.python.org/library/xmlrpclib.html
- uri = self.api.env.xmlrpc_uri
+ uri = self.env.xmlrpc_uri
if uri.startswith('https://'):
return xmlrpclib.ServerProxy(uri,
transport=KerbTransport(),
- #verbose=self.api.env.verbose,
)
return xmlrpclib.ServerProxy(uri)
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
new file mode 100644
index 000000000..c4662f84c
--- /dev/null
+++ b/ipalib/rpc.py
@@ -0,0 +1,86 @@
+# Authors:
+# Jason Gerard DeRose <jderose@redhat.com>
+#
+# 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
+
+"""
+Core RPC functionality.
+"""
+
+from types import NoneType
+from xmlrpclib import Binary
+
+
+def xmlrpc_wrap(value):
+ """
+ Wrap all ``str`` in ``xmlrpclib.Binary``.
+
+ Because ``xmlrpclib.dumps()`` will itself convert all ``unicode`` instances
+ into UTF-8 encoded ``str`` instances, we don't do it here.
+
+ So in total, when encoding data for an XML-RPC request, the following
+ transformations occur:
+
+ * All ``str`` instances are treated as binary data and are wrapped in
+ an ``xmlrpclib.Binary()`` instance.
+
+ * Only ``unicode`` instances are treated as character data. They get
+ converted to UTF-8 encoded ``str`` instances (although as mentioned,
+ not by this function).
+
+ Also see `xmlrpc_unwrap`.
+ """
+ if type(value) in (list, tuple):
+ return tuple(xmlrpc_wrap(v) for v in value)
+ if type(value) is dict:
+ return dict(
+ (k, xmlrpc_wrap(v)) for (k, v) in value.iteritems()
+ )
+ if type(value) is str:
+ return Binary(value)
+ assert type(value) in (unicode, int, float, bool, NoneType)
+ return value
+
+
+def xmlrpc_unwrap(value, encoding='UTF-8'):
+ """
+ Unwrap all ``xmlrpc.Binary``, decode all ``str`` into ``unicode``.
+
+ When decoding data from an XML-RPC request, the following transformations
+ occur:
+
+ * The binary payloads of all ``xmlrpclib.Binary`` instances are
+ returned as ``str`` instances.
+
+ * All ``str`` instances are treated as UTF-8 encoded character data.
+ They are decoded and the resulting ``unicode`` instance is returned.
+
+ Also see `xmlrpc_wrap`.
+ """
+ if type(value) in (list, tuple):
+ return tuple(xmlrpc_unwrap(v, encoding) for v in value)
+ if type(value) is dict:
+ return dict(
+ (k, xmlrpc_unwrap(v, encoding)) for (k, v) in value.iteritems()
+ )
+ if type(value) is str:
+ return value.decode(encoding)
+ if isinstance(value, Binary):
+ assert type(value.data) is str
+ return value.data
+ assert type(value) in (unicode, int, float, bool, NoneType)
+ return value
diff --git a/tests/test_ipalib/test_rpc.py b/tests/test_ipalib/test_rpc.py
new file mode 100644
index 000000000..14af2bf4e
--- /dev/null
+++ b/tests/test_ipalib/test_rpc.py
@@ -0,0 +1,123 @@
+# Authors:
+# Jason Gerard DeRose <jderose@redhat.com>
+#
+# 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
+
+"""
+Test the `ipalib.rpc` module.
+"""
+
+from xmlrpclib import Binary, dumps, loads
+import struct
+from tests.util import raises
+from ipalib import rpc
+
+
+# A string that should have bytes 'x\00' through '\xff':
+BINARY_BYTES = ''.join(struct.pack('B', d) for d in xrange(256))
+assert '\x00' in BINARY_BYTES and '\xff' in BINARY_BYTES
+assert type(BINARY_BYTES) is str and len(BINARY_BYTES) == 256
+
+# A UTF-8 encoded str
+UTF8_BYTES = '\xd0\x9f\xd0\xb0\xd0\xb2\xd0\xb5\xd0\xbb'
+
+# The same UTF-8 data decoded (a unicode instance)
+UNICODE_CHARS = u'\u041f\u0430\u0432\u0435\u043b'
+assert UTF8_BYTES.decode('UTF-8') == UNICODE_CHARS
+assert UNICODE_CHARS.encode('UTF-8') == UTF8_BYTES
+assert UTF8_BYTES != UNICODE_CHARS
+
+
+def dump_n_load(value):
+ (param, method) = loads(
+ dumps((value,))
+ )
+ return param[0]
+
+
+def round_trip(value):
+ return rpc.xmlrpc_unwrap(
+ dump_n_load(rpc.xmlrpc_wrap(value))
+ )
+
+
+def test_round_trip():
+ """
+ Test `ipalib.rpc.xmlrpc_wrap` and `ipalib.rpc.xmlrpc_unwrap`.
+
+ This tests the two functions together with ``xmlrpclib.dumps()`` and
+ ``xmlrpclib.loads()`` in a full encode/dumps/loads/decode round trip.
+ """
+ # We first test that our assumptions about xmlrpclib module in the Python
+ # standard library are correct:
+ assert dump_n_load(UTF8_BYTES) == UNICODE_CHARS
+ assert dump_n_load(UNICODE_CHARS) == UNICODE_CHARS
+ assert dump_n_load(Binary(BINARY_BYTES)).data == BINARY_BYTES
+ assert isinstance(dump_n_load(Binary(BINARY_BYTES)), Binary)
+ assert type(dump_n_load('hello')) is str
+ assert type(dump_n_load(u'hello')) is str
+
+ # Now we test our wrap and unwrap methods in combination with dumps, loads:
+ # All str should come back str (because they get wrapped in
+ # xmlrpclib.Binary(). All unicode should come back unicode because str
+ # explicity get decoded by rpc.xmlrpc_unwrap() if they weren't already
+ # decoded by xmlrpclib.loads().
+ assert round_trip(UTF8_BYTES) == UTF8_BYTES
+ assert round_trip(UNICODE_CHARS) == UNICODE_CHARS
+ assert round_trip(BINARY_BYTES) == BINARY_BYTES
+ assert type(round_trip('hello')) is str
+ assert type(round_trip(u'hello')) is unicode
+ assert round_trip('') == ''
+ assert round_trip(u'') == u''
+ compound = [UTF8_BYTES, UNICODE_CHARS, BINARY_BYTES,
+ dict(utf8=UTF8_BYTES, chars=UNICODE_CHARS, data=BINARY_BYTES)
+ ]
+ assert round_trip(compound) == tuple(compound)
+
+
+def test_xmlrpc_wrap():
+ """
+ Test the `ipalib.rpc.xmlrpc_wrap` function.
+ """
+ f = rpc.xmlrpc_wrap
+ assert f([]) == tuple()
+ assert f({}) == dict()
+ b = f('hello')
+ assert isinstance(b, Binary)
+ assert b.data == 'hello'
+ u = f(u'hello')
+ assert type(u) is unicode
+ assert u == u'hello'
+ value = f([dict(one=False, two=u'hello'), None, 'hello'])
+
+
+def test_xmlrpc_unwrap():
+ """
+ Test the `ipalib.rpc.xmlrpc_unwrap` function.
+ """
+ f = rpc.xmlrpc_unwrap
+ assert f([]) == tuple()
+ assert f({}) == dict()
+ value = f(Binary(UTF8_BYTES))
+ assert type(value) is str
+ assert value == UTF8_BYTES
+ assert f(UTF8_BYTES) == UNICODE_CHARS
+ assert f(UNICODE_CHARS) == UNICODE_CHARS
+ value = f([True, Binary('hello'), dict(one=1, two=UTF8_BYTES, three=None)])
+ assert value == (True, 'hello', dict(one=1, two=UNICODE_CHARS, three=None))
+ assert type(value[1]) is str
+ assert type(value[2]['two']) is unicode