mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2026-10-25 22:50:12 -05:00
Support API version-specific RPC marshalling.
Reviewed-By: Tomas Babej <tbabej@redhat.com>
This commit is contained in:
1 parent
4314d02fbf
commit
8b6dc819d5
3 files changed
+42
-32
No files matched your search
+14
-10
@@ -63,6 +63,7 @@ from ipapython.nsslib import NSSHTTPS, NSSConnection
|
||||
from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \
|
||||
KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, KRB5_REALM_CANT_RESOLVE
|
||||
from ipapython.dn import DN
|
||||
from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES
|
||||
|
||||
COOKIE_NAME = 'ipa_session'
|
||||
KEYRING_COOKIE_NAME = '%s_cookie:%%s' % COOKIE_NAME
|
||||
@@ -126,7 +127,7 @@ def delete_persistent_client_session_data(principal):
|
||||
# kernel_keyring only raises ValueError (why??)
|
||||
kernel_keyring.del_key(keyname)
|
||||
|
||||
def xml_wrap(value):
|
||||
def xml_wrap(value, version):
|
||||
"""
|
||||
Wrap all ``str`` in ``xmlrpclib.Binary``.
|
||||
|
||||
@@ -148,10 +149,10 @@ def xml_wrap(value):
|
||||
:param value: The simple scalar or simple compound value to wrap.
|
||||
"""
|
||||
if type(value) in (list, tuple):
|
||||
return tuple(xml_wrap(v) for v in value)
|
||||
return tuple(xml_wrap(v, version) for v in value)
|
||||
if isinstance(value, dict):
|
||||
return dict(
|
||||
(k, xml_wrap(v)) for (k, v) in value.iteritems()
|
||||
(k, xml_wrap(v, version)) for (k, v) in value.iteritems()
|
||||
)
|
||||
if type(value) is str:
|
||||
return Binary(value)
|
||||
@@ -199,7 +200,8 @@ def xml_unwrap(value, encoding='UTF-8'):
|
||||
return value
|
||||
|
||||
|
||||
def xml_dumps(params, methodname=None, methodresponse=False, encoding='UTF-8'):
|
||||
def xml_dumps(params, version, methodname=None, methodresponse=False,
|
||||
encoding='UTF-8'):
|
||||
"""
|
||||
Encode an XML-RPC data packet, transparently wraping ``params``.
|
||||
|
||||
@@ -219,7 +221,7 @@ def xml_dumps(params, methodname=None, methodresponse=False, encoding='UTF-8'):
|
||||
:param encoding: The Unicode encoding to use (defaults to ``'UTF-8'``).
|
||||
"""
|
||||
if type(params) is tuple:
|
||||
params = xml_wrap(params)
|
||||
params = xml_wrap(params, version)
|
||||
else:
|
||||
assert isinstance(params, Fault)
|
||||
return dumps(params,
|
||||
@@ -230,7 +232,7 @@ def xml_dumps(params, methodname=None, methodresponse=False, encoding='UTF-8'):
|
||||
)
|
||||
|
||||
|
||||
def json_encode_binary(val):
|
||||
def json_encode_binary(val, version):
|
||||
'''
|
||||
JSON cannot encode binary values. We encode binary values in Python str
|
||||
objects and text in Python unicode objects. In order to allow a binary
|
||||
@@ -253,10 +255,10 @@ def json_encode_binary(val):
|
||||
if isinstance(val, dict):
|
||||
new_dict = {}
|
||||
for k, v in val.items():
|
||||
new_dict[k] = json_encode_binary(v)
|
||||
new_dict[k] = json_encode_binary(v, version)
|
||||
return new_dict
|
||||
elif isinstance(val, (list, tuple)):
|
||||
new_list = [json_encode_binary(v) for v in val]
|
||||
new_list = [json_encode_binary(v, version) for v in val]
|
||||
return new_list
|
||||
elif isinstance(val, str):
|
||||
return {'__base64__': base64.b64encode(val)}
|
||||
@@ -894,7 +896,8 @@ class xmlclient(RPCClient):
|
||||
env_rpc_uri_key = 'xmlrpc_uri'
|
||||
|
||||
def _call_command(self, command, params):
|
||||
params = xml_wrap(params)
|
||||
version = params[1].get('version', VERSION_WITHOUT_CAPABILITIES)
|
||||
params = xml_wrap(params, version)
|
||||
result = command(*params)
|
||||
return xml_unwrap(result)
|
||||
|
||||
@@ -918,11 +921,12 @@ class JSONServerProxy(object):
|
||||
|
||||
def __request(self, name, args):
|
||||
payload = {'method': unicode(name), 'params': args, 'id': 0}
|
||||
version = args[1].get('version', VERSION_WITHOUT_CAPABILITIES)
|
||||
|
||||
response = self.__transport.request(
|
||||
self.__host,
|
||||
self.__handler,
|
||||
json.dumps(json_encode_binary(payload)),
|
||||
json.dumps(json_encode_binary(payload, version)),
|
||||
verbose=self.__verbose,
|
||||
)
|
||||
|
||||
|
||||
+13
-8
@@ -31,7 +31,8 @@ import urlparse
|
||||
import time
|
||||
import json
|
||||
|
||||
from ipalib import plugable, capabilities, errors
|
||||
from ipalib import plugable, errors
|
||||
from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES
|
||||
from ipalib.backend import Executioner
|
||||
from ipalib.errors import (PublicError, InternalError, CommandError, JSONError,
|
||||
CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError,
|
||||
@@ -378,7 +379,8 @@ class WSGIExecutioner(Executioner):
|
||||
name,
|
||||
type(e).__name__)
|
||||
|
||||
return self.marshal(result, error, _id)
|
||||
version = options.get('version', VERSION_WITHOUT_CAPABILITIES)
|
||||
return self.marshal(result, error, _id, version)
|
||||
|
||||
def simple_unmarshal(self, environ):
|
||||
name = environ['PATH_INFO'].strip('/')
|
||||
@@ -415,7 +417,8 @@ class WSGIExecutioner(Executioner):
|
||||
def unmarshal(self, data):
|
||||
raise NotImplementedError('%s.unmarshal()' % self.fullname)
|
||||
|
||||
def marshal(self, result, error, _id=None):
|
||||
def marshal(self, result, error, _id=None,
|
||||
version=VERSION_WITHOUT_CAPABILITIES):
|
||||
raise NotImplementedError('%s.marshal()' % self.fullname)
|
||||
|
||||
|
||||
@@ -439,7 +442,8 @@ class jsonserver(WSGIExecutioner, HTTP_Status):
|
||||
response = super(jsonserver, self).__call__(environ, start_response)
|
||||
return response
|
||||
|
||||
def marshal(self, result, error, _id=None):
|
||||
def marshal(self, result, error, _id=None,
|
||||
version=VERSION_WITHOUT_CAPABILITIES):
|
||||
if error:
|
||||
assert isinstance(error, PublicError)
|
||||
error = dict(
|
||||
@@ -455,7 +459,7 @@ class jsonserver(WSGIExecutioner, HTTP_Status):
|
||||
principal=unicode(principal),
|
||||
version=unicode(VERSION),
|
||||
)
|
||||
response = json_encode_binary(response)
|
||||
response = json_encode_binary(response, version)
|
||||
return json.dumps(response, sort_keys=True, indent=4)
|
||||
|
||||
def unmarshal(self, data):
|
||||
@@ -712,10 +716,11 @@ class xmlserver(KerberosWSGIExecutioner):
|
||||
# Keep backwards compatibility with client containing
|
||||
# bug https://fedorahosted.org/freeipa/ticket/3294:
|
||||
# If `version` is not given in XML-RPC, assume an old version
|
||||
options['version'] = capabilities.VERSION_WITHOUT_CAPABILITIES
|
||||
options['version'] = VERSION_WITHOUT_CAPABILITIES
|
||||
return (name, args, options, None)
|
||||
|
||||
def marshal(self, result, error, _id=None):
|
||||
def marshal(self, result, error, _id=None,
|
||||
version=VERSION_WITHOUT_CAPABILITIES):
|
||||
if error:
|
||||
self.debug('response: %s: %s', error.__class__.__name__, str(error))
|
||||
response = Fault(error.errno, error.strerror)
|
||||
@@ -723,7 +728,7 @@ class xmlserver(KerberosWSGIExecutioner):
|
||||
if isinstance(result, dict):
|
||||
self.debug('response: entries returned %d', result.get('count', 1))
|
||||
response = (result,)
|
||||
return xml_dumps(response, methodresponse=True)
|
||||
return xml_dumps(response, version, methodresponse=True)
|
||||
|
||||
|
||||
class jsonserver_session(jsonserver, KerberosSession):
|
||||
|
||||
@@ -29,6 +29,7 @@ from ipatests.data import binary_bytes, utf8_bytes, unicode_str
|
||||
from ipalib.frontend import Command
|
||||
from ipalib.request import context, Connection
|
||||
from ipalib import rpc, errors, api, request
|
||||
from ipapython.version import API_VERSION
|
||||
|
||||
|
||||
std_compound = (binary_bytes, utf8_bytes, unicode_str)
|
||||
@@ -43,7 +44,7 @@ def dump_n_load(value):
|
||||
|
||||
def round_trip(value):
|
||||
return rpc.xml_unwrap(
|
||||
dump_n_load(rpc.xml_wrap(value))
|
||||
dump_n_load(rpc.xml_wrap(value, API_VERSION))
|
||||
)
|
||||
|
||||
|
||||
@@ -90,15 +91,15 @@ def test_xml_wrap():
|
||||
Test the `ipalib.rpc.xml_wrap` function.
|
||||
"""
|
||||
f = rpc.xml_wrap
|
||||
assert f([]) == tuple()
|
||||
assert f({}) == dict()
|
||||
b = f('hello')
|
||||
assert f([], API_VERSION) == tuple()
|
||||
assert f({}, API_VERSION) == dict()
|
||||
b = f('hello', API_VERSION)
|
||||
assert isinstance(b, Binary)
|
||||
assert b.data == 'hello'
|
||||
u = f(u'hello')
|
||||
u = f(u'hello', API_VERSION)
|
||||
assert type(u) is unicode
|
||||
assert u == u'hello'
|
||||
value = f([dict(one=False, two=u'hello'), None, 'hello'])
|
||||
value = f([dict(one=False, two=u'hello'), None, 'hello'], API_VERSION)
|
||||
|
||||
|
||||
def test_xml_unwrap():
|
||||
@@ -127,14 +128,14 @@ def test_xml_dumps():
|
||||
params = (binary_bytes, utf8_bytes, unicode_str, None)
|
||||
|
||||
# Test serializing an RPC request:
|
||||
data = f(params, 'the_method')
|
||||
data = f(params, API_VERSION, 'the_method')
|
||||
(p, m) = loads(data)
|
||||
assert_equal(m, u'the_method')
|
||||
assert type(p) is tuple
|
||||
assert rpc.xml_unwrap(p) == params
|
||||
|
||||
# Test serializing an RPC response:
|
||||
data = f((params,), methodresponse=True)
|
||||
data = f((params,), API_VERSION, methodresponse=True)
|
||||
(tup, m) = loads(data)
|
||||
assert m is None
|
||||
assert len(tup) == 1
|
||||
@@ -143,7 +144,7 @@ def test_xml_dumps():
|
||||
|
||||
# Test serializing an RPC response containing a Fault:
|
||||
fault = Fault(69, unicode_str)
|
||||
data = f(fault, methodresponse=True)
|
||||
data = f(fault, API_VERSION, methodresponse=True)
|
||||
e = raises(Fault, loads, data)
|
||||
assert e.faultCode == 69
|
||||
assert_equal(e.faultString, unicode_str)
|
||||
@@ -155,7 +156,7 @@ def test_xml_loads():
|
||||
"""
|
||||
f = rpc.xml_loads
|
||||
params = (binary_bytes, utf8_bytes, unicode_str, None)
|
||||
wrapped = rpc.xml_wrap(params)
|
||||
wrapped = rpc.xml_wrap(params, API_VERSION)
|
||||
|
||||
# Test un-serializing an RPC request:
|
||||
data = dumps(wrapped, 'the_method', allow_none=True)
|
||||
@@ -210,19 +211,19 @@ class test_xmlclient(PluginTester):
|
||||
conn = DummyClass(
|
||||
(
|
||||
'user_add',
|
||||
rpc.xml_wrap(params),
|
||||
rpc.xml_wrap(params, API_VERSION),
|
||||
{},
|
||||
rpc.xml_wrap(result),
|
||||
rpc.xml_wrap(result, API_VERSION),
|
||||
),
|
||||
(
|
||||
'user_add',
|
||||
rpc.xml_wrap(params),
|
||||
rpc.xml_wrap(params, API_VERSION),
|
||||
{},
|
||||
Fault(3007, u"'four' is required"), # RequirementError
|
||||
),
|
||||
(
|
||||
'user_add',
|
||||
rpc.xml_wrap(params),
|
||||
rpc.xml_wrap(params, API_VERSION),
|
||||
{},
|
||||
Fault(700, u'no such error'), # There is no error 700
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user