On 01/13/2014 05:11 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
See commit message & ticket.
https://fedorahosted.org/freeipa/ticket/4069
Our handling of XML-RPC introspection is iffy as it is and this would
remove those methods completely. Can you add them back into the
xmlserver class?
Not just iffy, it's non-existent. See
https://fedorahosted.org/freeipa/ticket/2937.
The patch removes the __system dict which has been unused since at least
Jason's web UI work in 2009, Git tells me. The patch did leave the
actual methods.
Well, actually it's not that hard to get these working again; see
attached patch (which can be reviewed separately).
--
PetrĀ³
P.S. to test raw XML, you can use:
curl -v \
-H "referer:https://`hostname`/ipa" \
-H "Content-Type:text/xml" \
-H "Accept:applicaton/xml" \
--negotiate -u : \
--delegation always \
--cacert /etc/ipa/ca.crt \
-d "<?xml version='1.0' encoding='UTF-8'?>
<methodCall>
<methodName>system.methodHelp</methodName>
<params>
<param><type>string</type><value>ping</value></param>
</params>
</methodCall>" -X POST https://`hostname`/ipa/xml
From 5b85bb1afc5112e79177bd5995712b9a202661cb Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pvikt...@redhat.com>
Date: Tue, 14 Jan 2014 13:41:19 +0100
Subject: [PATCH] Implement XML introspection
https://fedorahosted.org/freeipa/ticket/2937
---
ipaserver/rpcserver.py | 57 ++++++++++++++++++++++---
ipatests/test_ipalib/test_rpc.py | 92 ++++++++++++++++++++++++++++++++++++++--
2 files changed, 140 insertions(+), 9 deletions(-)
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index a2a9db8ae4b0bac8402b7bee1caae45d27ce450a..05fbdfedb7871f48286c6414ae1b2392d449f17d 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -31,7 +31,7 @@
import time
import json
-from ipalib import plugable, capabilities
+from ipalib import plugable, capabilities, errors
from ipalib.backend import Executioner
from ipalib.errors import (PublicError, InternalError, CommandError, JSONError,
CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError,
@@ -290,6 +290,8 @@ class WSGIExecutioner(Executioner):
content_type = None
key = ''
+ _system_commands = {}
+
def set_api(self, api):
super(WSGIExecutioner, self).set_api(api)
if 'wsgi_dispatch' in self.api.Backend:
@@ -331,9 +333,12 @@ def wsgi_execute(self, environ):
(name, args, options, _id) = self.unmarshal(data)
else:
(name, args, options, _id) = self.simple_unmarshal(environ)
- if name not in self.Command:
+ if name in self._system_commands:
+ result = self._system_commands[name](self, *args, **options)
+ elif name not in self.Command:
raise CommandError(name=name)
- result = self.Command[name](*args, **options)
+ else:
+ result = self.Command[name](*args, **options)
except PublicError, e:
error = e
except StandardError, e:
@@ -650,16 +655,56 @@ class xmlserver(KerberosWSGIExecutioner):
key = '/xml'
def listMethods(self, *params):
- return tuple(name.decode('UTF-8') for name in self.Command)
+ """list methods for XML-RPC introspection"""
+ if params:
+ raise errors.ZeroArgumentError(name='system.listMethods')
+ return (tuple(unicode(name) for name in self.Command) +
+ tuple(unicode(name) for name in self._system_commands))
+
+ def _get_method_name(self, name, *params):
+ """Get a method name for XML-RPC introspection commands"""
+ if not params:
+ raise errors.RequirementError(name='method name')
+ elif len(params) > 1:
+ raise errors.MaxArgumentError(name=name, count=1)
+ [method_name] = params
+ return method_name
def methodSignature(self, *params):
- return u'methodSignature not implemented'
+ """get method signature for XML-RPC introspection"""
+ method_name = self._get_method_name('system.methodSignature', *params)
+ if method_name in self._system_commands:
+ # TODO
+ # for now let's not go out of our way to document standard XML-RPC
+ return u'undef'
+ elif method_name in self.Command:
+ # All IPA commands return a dict (struct),
+ # and take a params, options - list and dict (array, struct)
+ return [u'struct', u'array', u'struct']
+ else:
+ raise errors.CommandError(name=method_name)
def methodHelp(self, *params):
- return u'methodHelp not implemented'
+ """get method docstring for XML-RPC introspection"""
+ method_name = self._get_method_name('system.methodHelp', *params)
+ if method_name in self._system_commands:
+ return u''
+ elif method_name in self.Command:
+ return unicode(self.Command[method_name].__doc__ or '')
+ else:
+ raise errors.CommandError(name=method_name)
+
+ _system_commands = {
+ 'system.listMethods': listMethods,
+ 'system.methodSignature': methodSignature,
+ 'system.methodHelp': methodHelp,
+ }
def unmarshal(self, data):
(params, name) = xml_loads(data)
+ if name in self._system_commands:
+ # For XML-RPC introspection, return params directly
+ return (name, params, {}, None)
(args, options) = params_2_args_options(params)
if 'version' not in options:
# Keep backwards compatibility with client containing
diff --git a/ipatests/test_ipalib/test_rpc.py b/ipatests/test_ipalib/test_rpc.py
index 56b8184cf787b842f4818ff9f4fea6ca151926fa..c874262a45cd8688938acddc849c973e6257d2cf 100644
--- a/ipatests/test_ipalib/test_rpc.py
+++ b/ipatests/test_ipalib/test_rpc.py
@@ -21,13 +21,14 @@
Test the `ipalib.rpc` module.
"""
-import threading
-from xmlrpclib import Binary, Fault, dumps, loads, ServerProxy
+from xmlrpclib import Binary, Fault, dumps, loads
+
+import nose
from ipatests.util import raises, assert_equal, PluginTester, DummyClass
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
+from ipalib import rpc, errors, api, request
std_compound = (binary_bytes, utf8_bytes, unicode_str)
@@ -242,3 +243,88 @@ class user_add(Command):
assert_equal(e.error, u'no such error')
assert context.xmlclient.conn._calledall() is True
+
+
+class test_xml_introspection(object):
+ @classmethod
+ def setUpClass(self):
+ try:
+ api.Backend.xmlclient.connect(fallback=False)
+ except (errors.NetworkError, IOError):
+ raise nose.SkipTest('%r: Server not available: %r' %
+ (__name__, api.env.xmlrpc_uri))
+
+ @classmethod
+ def tearDownClass(self):
+ request.destroy_context()
+
+ def test_list_methods(self):
+ result = api.Backend.xmlclient.conn.system.listMethods()
+ assert len(result)
+ assert 'ping' in result
+ assert 'user_add' in result
+ assert 'system.listMethods' in result
+ assert 'system.methodSignature' in result
+ assert 'system.methodHelp' in result
+
+ def test_list_methods_many_params(self):
+ try:
+ result = api.Backend.xmlclient.conn.system.listMethods('foo')
+ except Fault, f:
+ print f
+ assert f.faultCode == 3003
+ assert f.faultString == (
+ "command 'system.listMethods' takes no arguments")
+ else:
+ raise AssertionError('did not raise')
+
+ def test_ping_signature(self):
+ result = api.Backend.xmlclient.conn.system.methodSignature('ping')
+ assert result == ['struct', 'array', 'struct']
+
+
+ def test_ping_help(self):
+ result = api.Backend.xmlclient.conn.system.methodHelp('ping')
+ assert result == 'Ping a remote server.'
+
+ def test_signature_no_params(self):
+ try:
+ result = api.Backend.xmlclient.conn.system.methodSignature()
+ except Fault, f:
+ print f
+ assert f.faultCode == 3007
+ assert f.faultString == "'method name' is required"
+ else:
+ raise AssertionError('did not raise')
+
+ def test_signature_many_params(self):
+ try:
+ result = api.Backend.xmlclient.conn.system.methodSignature('a', 'b')
+ except Fault, f:
+ print f
+ assert f.faultCode == 3004
+ assert f.faultString == (
+ "command 'system.methodSignature' takes at most 1 argument")
+ else:
+ raise AssertionError('did not raise')
+
+ def test_help_no_params(self):
+ try:
+ result = api.Backend.xmlclient.conn.system.methodHelp()
+ except Fault, f:
+ print f
+ assert f.faultCode == 3007
+ assert f.faultString == "'method name' is required"
+ else:
+ raise AssertionError('did not raise')
+
+ def test_help_many_params(self):
+ try:
+ result = api.Backend.xmlclient.conn.system.methodHelp('a', 'b')
+ except Fault, f:
+ print f
+ assert f.faultCode == 3004
+ assert f.faultString == (
+ "command 'system.methodHelp' takes at most 1 argument")
+ else:
+ raise AssertionError('did not raise')
--
1.8.4.2
_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel