summaryrefslogtreecommitdiffstats
path: root/ipaserver/rpcserver.py
diff options
context:
space:
mode:
authorPetr Viktorin <pviktori@redhat.com>2012-12-19 04:25:24 -0500
committerPetr Viktorin <pviktori@redhat.com>2013-11-26 16:59:59 +0100
commit1e836d2d0c8916f5b8a352cc8395048f1147554d (patch)
tree6c13ccd9083803e3945d40516d2add39533a4929 /ipaserver/rpcserver.py
parenta1165ffbb80446890e3757113c9682c8526ed666 (diff)
downloadfreeipa-1e836d2d0c8916f5b8a352cc8395048f1147554d.tar.gz
freeipa-1e836d2d0c8916f5b8a352cc8395048f1147554d.tar.xz
freeipa-1e836d2d0c8916f5b8a352cc8395048f1147554d.zip
Switch client to JSON-RPC
Modify ipalib.rpc to support JSON-RPC in addition to XML-RPC. This is done by subclassing and extending xmlrpclib, because our existing code relies on xmlrpclib internals. The URI to use is given in the new jsonrpc_uri env variable. When it is not given, it is generated from xmlrpc_uri by replacing /xml with /json. The rpc_json_uri env variable existed before, but was unused, undocumented and not set the install scripts. This patch removes it in favor of jsonrpc_uri (for consistency with xmlrpc_uri). Add the rpc_protocol env variable to control the protocol IPA uses. rpc_protocol defaults to 'jsonrpc', but may be changed to 'xmlrpc'. Make backend.Executioner and tests use the backend specified by rpc_protocol. For compatibility with unwrap_xml, decoding JSON now gives tuples instead of lists. Design: http://freeipa.org/page/V3/JSON-RPC Ticket: https://fedorahosted.org/freeipa/ticket/3299
Diffstat (limited to 'ipaserver/rpcserver.py')
-rw-r--r--ipaserver/rpcserver.py113
1 files changed, 11 insertions, 102 deletions
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index 0ec7b02d2..496430597 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -25,27 +25,29 @@ Also see the `ipalib.rpc` module.
from xml.sax.saxutils import escape
from xmlrpclib import Fault
-from wsgiref.util import shift_path_info
-import base64
import os
-import string
import datetime
-from decimal import Decimal
import urlparse
import time
import json
from ipalib import plugable, capabilities
from ipalib.backend import Executioner
-from ipalib.errors import PublicError, InternalError, CommandError, JSONError, ConversionError, CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError, ExecutionError
-from ipalib.request import context, Connection, destroy_context
-from ipalib.rpc import xml_dumps, xml_loads
+from ipalib.errors import (PublicError, InternalError, CommandError, JSONError,
+ CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError,
+ ExecutionError)
+from ipalib.request import context, destroy_context
+from ipalib.rpc import (xml_dumps, xml_loads,
+ json_encode_binary, json_decode_binary)
from ipalib.util import parse_time_duration, normalize_name
from ipapython.dn import DN
from ipaserver.plugins.ldap2 import ldap2
-from ipalib.session import session_mgr, AuthManager, get_ipa_ccache_name, load_ccache_data, bind_ipa_ccache, release_ipa_ccache, fmt_time, default_max_session_duration
+from ipalib.session import (session_mgr, AuthManager, get_ipa_ccache_name,
+ load_ccache_data, bind_ipa_ccache, release_ipa_ccache, fmt_time,
+ default_max_session_duration)
from ipalib.backend import Backend
-from ipalib.krb_utils import krb5_parse_ccache, KRB5_CCache, krb_ticket_expiration_threshold, krb5_format_principal_name
+from ipalib.krb_utils import (
+ KRB5_CCache, krb_ticket_expiration_threshold, krb5_format_principal_name)
from ipapython import ipautil
from ipapython.version import VERSION
from ipalib.text import _
@@ -397,99 +399,6 @@ class WSGIExecutioner(Executioner):
raise NotImplementedError('%s.marshal()' % self.fullname)
-def json_encode_binary(val):
- '''
- 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
- object to be passed through JSON we base64 encode it thus converting it to
- text which JSON can transport. To assure we recognize the value is a base64
- encoded representation of the original binary value and not confuse it with
- other text we convert the binary value to a dict in this form:
-
- {'__base64__' : base64_encoding_of_binary_value}
-
- This modification of the original input value cannot be done "in place" as
- one might first assume (e.g. replacing any binary items in a container
- (e.g. list, tuple, dict) with the base64 dict because the container might be
- an immutable object (i.e. a tuple). Therefore this function returns a copy
- of any container objects it encounters with tuples replaced by lists. This
- is O.K. because the JSON encoding will map both lists and tuples to JSON
- arrays.
- '''
-
- if isinstance(val, dict):
- new_dict = {}
- for k,v in val.items():
- new_dict[k] = json_encode_binary(v)
- return new_dict
- elif isinstance(val, (list, tuple)):
- new_list = [json_encode_binary(v) for v in val]
- return new_list
- elif isinstance(val, str):
- return {'__base64__' : base64.b64encode(val)}
- elif isinstance(val, Decimal):
- return {'__base64__' : base64.b64encode(str(val))}
- elif isinstance(val, DN):
- return str(val)
- else:
- return val
-
-def json_decode_binary(val):
- '''
- JSON cannot transport binary data. In order to transport binary data we
- convert binary data to a form like this:
-
- {'__base64__' : base64_encoding_of_binary_value}
-
- see json_encode_binary()
-
- After JSON had decoded the JSON stream back into a Python object we must
- recursively scan the object looking for any dicts which might represent
- binary values and replace the dict containing the base64 encoding of the
- binary value with the decoded binary value. Unlike the encoding problem
- where the input might consist of immutable object, all JSON decoded
- container are mutable so the conversion could be done in place. However we
- don't modify objects in place because of side effects which may be
- dangerous. Thus we elect to spend a few more cycles and avoid the
- possibility of unintended side effects in favor of robustness.
- '''
-
- if isinstance(val, dict):
- if val.has_key('__base64__'):
- return base64.b64decode(val['__base64__'])
- else:
- new_dict = {}
- for k,v in val.items():
- if isinstance(v, dict) and v.has_key('__base64__'):
- new_dict[k] = base64.b64decode(v['__base64__'])
- else:
- new_dict[k] = json_decode_binary(v)
- return new_dict
- elif isinstance(val, list):
- new_list = []
- n = len(val)
- i = 0
- while i < n:
- v = val[i]
- if isinstance(v, dict) and v.has_key('__base64__'):
- binary_val = base64.b64decode(v['__base64__'])
- new_list.append(binary_val)
- else:
- new_list.append(json_decode_binary(v))
- i += 1
- return new_list
- else:
- if isinstance(val, basestring):
- try:
- return val.decode('utf-8')
- except UnicodeDecodeError:
- raise ConversionError(
- name=val,
- error='incorrect type'
- )
- else:
- return val
-
class jsonserver(WSGIExecutioner, HTTP_Status):
"""
JSON RPC server.