The final part of rejecting unknown Command arguments: enable the validation, add tests.
Also fix up things that were changed since the previous patches.

https://fedorahosted.org/freeipa/ticket/2509

--
PetrĀ³
From 64496d35b5483b8b237282dd157388f10e72beda Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pvikt...@redhat.com>
Date: Tue, 17 Apr 2012 12:42:35 -0400
Subject: [PATCH] Fail on unknown Command options

When unknown keyword arguments are passed to a Command, raise an
error instead of ignoring them.

Options used when IPA calls its commands internally are listed
in a new Command attribute called internal_options, and allowed.

Previous patches (0b01751c, c45174d6, c5689e7f) made IPA not use
unknown keyword arguments in its own commands and tests, but since
that some violations were reintroduced in permission_find and tests.
Fix those.

Tests included; both a frontend unittest and a XML-RPC test via the
ping plugin (which was untested previously).

https://fedorahosted.org/freeipa/ticket/2509
---
 ipalib/frontend.py                        |   13 ++++++--
 ipalib/plugins/aci.py                     |    2 ++
 ipalib/plugins/automount.py               |    4 +++
 ipalib/plugins/permission.py              |   17 +++++-----
 tests/test_cmdline/test_cli.py            |    6 ++--
 tests/test_ipalib/test_frontend.py        |    5 +++
 tests/test_xmlrpc/test_host_plugin.py     |    2 +-
 tests/test_xmlrpc/test_netgroup_plugin.py |    6 ++--
 tests/test_xmlrpc/test_ping_plugin.py     |   52 +++++++++++++++++++++++++++++
 9 files changed, 89 insertions(+), 18 deletions(-)
 create mode 100644 tests/test_xmlrpc/test_ping_plugin.py

diff --git a/ipalib/frontend.py b/ipalib/frontend.py
index e8a84eabe0ccb981eabcc6b5d5e89f80c4135fe8..7c63b27ddb41e751692e47ebc334cb699606a74e 100644
--- a/ipalib/frontend.py
+++ b/ipalib/frontend.py
@@ -29,7 +29,8 @@
 from output import Output, Entry, ListOfEntries
 from text import _, ngettext
 
-from errors import ZeroArgumentError, MaxArgumentError, OverlapError, RequiresRoot, VersionError, RequirementError
+from errors import (ZeroArgumentError, MaxArgumentError, OverlapError,
+    RequiresRoot, VersionError, RequirementError, OptionError)
 from errors import InvocationError
 from constants import TYPE_ERROR
 from ipapython.version import API_VERSION
@@ -404,6 +405,8 @@ class Command(HasParam):
     output_params = Plugin.finalize_attr('output_params')
     has_output_params = tuple()
 
+    internal_options = tuple()
+
     msg_summary = None
     msg_truncated = _('Results are truncated, try a more specific search')
 
@@ -520,7 +523,13 @@ def __args_2_params(self, values):
     def __options_2_params(self, options):
         for name in self.params:
             if name in options:
-                yield (name, options[name])
+                yield (name, options.pop(name))
+        # If any options remain, they are either internal or unknown
+        unused_keys = set(options).difference(self.internal_options)
+        unused_keys.discard('version')
+        if unused_keys:
+            raise OptionError(_('Unknown option: %(option)s'),
+                option=unused_keys.pop())
 
     def args_options_2_entry(self, *args, **options):
         """
diff --git a/ipalib/plugins/aci.py b/ipalib/plugins/aci.py
index b0be26f5c44e51d91af3bdf7d0a94c0a14f8fe4a..8476f1826561f8f00522b0b19f057d493bec2611 100644
--- a/ipalib/plugins/aci.py
+++ b/ipalib/plugins/aci.py
@@ -610,6 +610,8 @@ class aci_mod(crud.Update):
 
     takes_options = (_prefix_option,)
 
+    internal_options = ['rename']
+
     msg_summary = _('Modified ACI "%(value)s"')
 
     def execute(self, aciname, **kw):
diff --git a/ipalib/plugins/automount.py b/ipalib/plugins/automount.py
index ac9c9769f26b31631322e725721ecaf470b80f1c..a42c53b6fcada362a47657c8d6ea3f652240b4d4 100644
--- a/ipalib/plugins/automount.py
+++ b/ipalib/plugins/automount.py
@@ -787,6 +787,8 @@ class automountkey_add(LDAPCreate):
 
     msg_summary = _('Added automount key "%(value)s"')
 
+    internal_options = ['description', 'add_operation']
+
     def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
         options.pop('add_operation', None)
         options.pop('description', None)
@@ -910,6 +912,8 @@ class automountkey_mod(LDAPUpdate):
 
     msg_summary = _('Modified automount key "%(value)s"')
 
+    internal_options = ['newautomountkey']
+
     takes_options = LDAPUpdate.takes_options + (
         IA5Str('newautomountinformation?',
                cli_name='newinfo',
diff --git a/ipalib/plugins/permission.py b/ipalib/plugins/permission.py
index ff38f852da9a6d133ae0b4c7a77c74fcd4a23913..c532e16f4e29066708ac2c0d6ee1d4de204cb631 100644
--- a/ipalib/plugins/permission.py
+++ b/ipalib/plugins/permission.py
@@ -359,10 +359,16 @@ class permission_find(LDAPSearch):
     def post_callback(self, ldap, entries, truncated, *args, **options):
         if options.pop('pkey_only', False):
             return
+
+        # There is an option/param overlap: "cn" must be passed as "aciname"
+        # to aci-find. Besides that we don't need cn anymore so pop it
+        aciname = options.pop('cn', None)
+
         for entry in entries:
             (dn, attrs) = entry
             try:
-                aci = self.api.Command.aci_show(attrs['cn'][0], aciprefix=ACI_PREFIX, **options)['result']
+                aci = self.api.Command.aci_show(
+                    attrs['cn'][0], aciprefix=ACI_PREFIX, **options)['result']
 
                 # copy information from respective ACI to permission entry
                 for attr in self.obj.aci_attributes:
@@ -375,23 +381,18 @@ def post_callback(self, ldap, entries, truncated, *args, **options):
         # aren't already in the list along with their permission info.
 
         opts = copy.copy(options)
+        if aciname:
+            opts['aciname'] = aciname
         opts['aciprefix'] = ACI_PREFIX
         try:
             # permission ACI attribute is needed
             del opts['raw']
         except:
             pass
-        if 'cn' in options:
-            # the attribute for name is difference in acis
-            opts['aciname'] = options['cn']
         aciresults = self.api.Command.aci_find(*args, **opts)
         truncated = truncated or aciresults['truncated']
         results = aciresults['result']
 
-        if 'cn' in options:
-            # there is an option/param overlap if --name is in the
-            # search list, we don't need cn anymore so drop it.
-            options.pop('cn')
         for aci in results:
             found = False
             if 'permission' in aci:
diff --git a/tests/test_cmdline/test_cli.py b/tests/test_cmdline/test_cli.py
index 095577a3b776341d5571f82ecbab55e7e3527f8e..d961f8725b5e4360b6c2298f3fddd3589cbb310d 100644
--- a/tests/test_cmdline/test_cli.py
+++ b/tests/test_cmdline/test_cli.py
@@ -128,8 +128,7 @@ def test_dnsrecord_add(self):
     def test_dnsrecord_del_all(self):
         try:
             self.run_command('dnszone_add', idnsname=u'test-example.com',
-                idnssoamname=u'ns.test-example.com',
-                admin_email=u'devn...@test-example.com', force=True)
+                idnssoamname=u'ns.test-example.com', force=True)
         except errors.NotFound:
             raise nose.SkipTest('DNS is not configured')
         try:
@@ -162,8 +161,7 @@ def test_dnsrecord_del_all(self):
     def test_dnsrecord_del_one_by_one(self):
         try:
             self.run_command('dnszone_add', idnsname=u'test-example.com',
-                idnssoamname=u'ns.test-example.com',
-                admin_email=u'devn...@test-example.com', force=True)
+                idnssoamname=u'ns.test-example.com', force=True)
         except errors.NotFound:
             raise nose.SkipTest('DNS is not configured')
         try:
diff --git a/tests/test_ipalib/test_frontend.py b/tests/test_ipalib/test_frontend.py
index b717a43ad4c68940582f901308f4dd4da77834ee..5f7ce65fb29d8eed531d15bf3e3fb094933aa007 100644
--- a/tests/test_ipalib/test_frontend.py
+++ b/tests/test_ipalib/test_frontend.py
@@ -511,6 +511,11 @@ def test_args_options_2_params(self):
         assert e.count == 2
         assert str(e) == "command 'example' takes at most 2 arguments"
 
+        # Test that OptionError is raised when an extra option is given:
+        o = self.get_instance()
+        e = raises(errors.OptionError, o.args_options_2_params, bad_option=True)
+        assert e.option == 'bad_option'
+
         # Test that OverlapError is raised:
         o = self.get_instance(args=('one', 'two'), options=('three', 'four'))
         e = raises(errors.OverlapError, o.args_options_2_params,
diff --git a/tests/test_xmlrpc/test_host_plugin.py b/tests/test_xmlrpc/test_host_plugin.py
index 8798168afa71653b64870c77d11a7fa81ec4c952..69ef82e20dafdfed38669ec36c05a5055754b06c 100644
--- a/tests/test_xmlrpc/test_host_plugin.py
+++ b/tests/test_xmlrpc/test_host_plugin.py
@@ -126,7 +126,7 @@ class test_host(Declarative):
             command=('host_add', [fqdn1],
                 dict(
                     description=u'Test host 1',
-                    locality=u'Undisclosed location 1',
+                    l=u'Undisclosed location 1',
                     force=True,
                 ),
             ),
diff --git a/tests/test_xmlrpc/test_netgroup_plugin.py b/tests/test_xmlrpc/test_netgroup_plugin.py
index d51287bcd0174818126131c1bebcb558720a0cc7..951bc77a39d68b9e1d1bfec75551ba550fff5858 100644
--- a/tests/test_xmlrpc/test_netgroup_plugin.py
+++ b/tests/test_xmlrpc/test_netgroup_plugin.py
@@ -726,7 +726,7 @@ class test_netgroup(Declarative):
 
 
         dict(
-            desc='Add duplicatehost %r to netgroup %r' % (host1, netgroup1),
+            desc='Add duplicate host %r to netgroup %r' % (host1, netgroup1),
             command=(
                 'netgroup_add_member', [netgroup1], dict(host=host1)
             ),
@@ -960,8 +960,8 @@ class test_netgroup(Declarative):
         ),
 
         dict(
-            desc='Search for all netgroups using empty memberuser',
-            command=('netgroup_find', [], dict(memberuser=None)),
+            desc='Search for all netgroups using empty member user',
+            command=('netgroup_find', [], dict(user=None)),
             expected=dict(
                 count=2,
                 truncated=False,
diff --git a/tests/test_xmlrpc/test_ping_plugin.py b/tests/test_xmlrpc/test_ping_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..284aed54f314f847f678e7033a4a55bfa2d2fa2f
--- /dev/null
+++ b/tests/test_xmlrpc/test_ping_plugin.py
@@ -0,0 +1,52 @@
+# Authors:
+#   Petr Viktorin <pvikt...@redhat.com>
+#
+# Copyright (C) 2012  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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+"""
+Test the `ipalib/plugins/ping.py` module, and XML-RPC in general.
+"""
+
+from ipalib import api, errors, _
+from tests.util import assert_equal, Fuzzy
+from xmlrpc_test import Declarative
+
+
+class test_ping(Declarative):
+
+    tests = [
+        dict(
+            desc='Ping the server',
+            command=('ping', [], {}),
+            expected=dict(
+                summary=Fuzzy('IPA server version .*. API version .*')),
+        ),
+
+        dict(
+            desc='Try to ping with an argument',
+            command=('ping', ['bad_arg'], {}),
+            expected=errors.ZeroArgumentError(name='ping'),
+        ),
+
+        dict(
+            desc='Try to ping with an option',
+            command=('ping', [], dict(bad_arg=True)),
+            expected=errors.OptionError(_('Unknown option: %(option)s'),
+                option='bad_arg'),
+        ),
+
+    ]
-- 
1.7.10.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to