URL: https://github.com/freeipa/freeipa/pull/6031
Author: rcritten
 Title: #6031: Improve sudooption docs, make the option multi-value
Action: opened

PR body:
"""
I don't know why this wasn't always multi-value but if one wanted
to set multiple options they needed to call add-option multiple
times. The LDAP attribute is already multi-value.

This shouldn't cause API issues as it understood the attribute as
multi-value just didn't expose it. Client output on the CLI will
look a bit different:

Added option "('one', 'two')" to Sudo Rule "test"

or

Added option "(u'one', u'Two')" to Sudo Rule "test"

instead of with this change:

Added option "one,two" to Sudo Rule "test"

https://pagure.io/freeipa/issue/2278

Signed-off-by: Rob Crittenden <rcrit...@redhat.com>
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/6031/head:pr6031
git checkout pr6031
From 1f860a3d82fc66dd165fbd5fd52a279a4ef013dc Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcrit...@redhat.com>
Date: Mon, 27 Sep 2021 16:54:05 -0400
Subject: [PATCH] Improve sudooption docs, make the option multi-value

I don't know why this wasn't always multi-value but if one wanted
to set multiple options they needed to call add-option multiple
times. The LDAP attribute is already multi-value.

This shouldn't cause API issues as it understood the attribute as
multi-value just didn't expose it. Client output on the CLI will
look a bit different:

Added option "('one', 'two')" to Sudo Rule "test"

or

Added option "(u'one', u'Two')" to Sudo Rule "test"

instead of with this change:

Added option "one,two" to Sudo Rule "test"

https://pagure.io/freeipa/issue/2278

Signed-off-by: Rob Crittenden <rcrit...@redhat.com>
---
 ipaclient/plugins/sudorule.py                |  3 ++-
 ipaserver/plugins/sudorule.py                | 26 ++++++++++++++------
 ipatests/test_xmlrpc/test_sudorule_plugin.py | 25 +++++++++++++++++++
 3 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/ipaclient/plugins/sudorule.py b/ipaclient/plugins/sudorule.py
index a87628044cb..9e377fc2e57 100644
--- a/ipaclient/plugins/sudorule.py
+++ b/ipaclient/plugins/sudorule.py
@@ -41,7 +41,8 @@ class sudorule_add_option(MethodOverride):
     def output_for_cli(self, textui, result, cn, **options):
         textui.print_dashed(
             _('Added option "%(option)s" to Sudo Rule "%(rule)s"')
-              % dict(option=options['ipasudoopt'], rule=cn))
+            % dict(option=','.join(options['ipasudoopt']), rule=cn)
+        )
 
         super(sudorule_add_option, self).output_for_cli(textui, result, cn,
                                                         **options)
diff --git a/ipaserver/plugins/sudorule.py b/ipaserver/plugins/sudorule.py
index 68806571552..b5fc9bcab0c 100644
--- a/ipaserver/plugins/sudorule.py
+++ b/ipaserver/plugins/sudorule.py
@@ -57,6 +57,11 @@
    RunAsGroup: The group(s) whose gid rights Sudo will be invoked with.
    Options: The various Sudoers Options that can modify Sudo's behavior.
 """) + _("""
+Each option needs to be added separately and no validation is done whether
+the option is known by sudo or is in a valid format. Environment variables
+also need to be set individually. For example env_keep="FOO BAR" in sudoers
+needs be represented as --sudooption env_keep=FOO --sudooption env_keep+=BAR.
+""") + _("""
 An order can be added to a sudorule to control the order in which they
 are evaluated (if the client supports it). This order is an integer and
 must be unique.
@@ -89,6 +94,10 @@
 """) + _("""
  Set a default Sudo option:
    ipa sudorule-add-option defaults --sudooption '!authenticate'
+""") + _("""
+ Set multiple default Sudo options:
+   ipa sudorule-add-option defaults --sudooption '!authenticate' \
+   --sudooption mail_badpass
 """) + _("""
  Set SELinux type and role transitions on a rule:
    ipa sudorule-add-option sysadmin_sudo --sudooption type=unconfined_t
@@ -353,7 +362,7 @@ class sudorule(LDAPObject):
             label=_('RunAs External Group'),
             doc=_('External Group the commands can run as (sudorule-find only)'),
         ),
-        Str('ipasudoopt?',
+        Str('ipasudoopt*',
             label=_('Sudo Option'),
             flags=['no_create', 'no_update', 'no_search'],
         ),
@@ -989,7 +998,7 @@ class sudorule_add_option(LDAPQuery):
 
     has_output = output.standard_entry
     takes_options = (
-        Str('ipasudoopt',
+        Str('ipasudoopt+',
             cli_name='sudooption',
             label=_('Sudo Option'),
         ),
@@ -1000,16 +1009,17 @@ def execute(self, cn, **options):
 
         dn = self.obj.get_dn(cn)
 
-        if not options['ipasudoopt'].strip():
+        if len(options.get('ipasudoopt',[])) == 0:
             raise errors.EmptyModlist()
         entry_attrs = ldap.get_entry(dn, ['ipasudoopt'])
 
         try:
-            if options['ipasudoopt'] not in entry_attrs['ipasudoopt']:
-                entry_attrs.setdefault('ipasudoopt', []).append(
-                    options['ipasudoopt'])
-            else:
-                raise errors.DuplicateEntry
+            entry_attrs.setdefault('ipasudoopt', [])
+            for option in options['ipasudoopt']:
+                if option not in entry_attrs['ipasudoopt']:
+                    entry_attrs['ipasudoopt'].append(option)
+                else:
+                    raise errors.DuplicateEntry
         except KeyError:
             entry_attrs.setdefault('ipasudoopt', []).append(
                 options['ipasudoopt'])
diff --git a/ipatests/test_xmlrpc/test_sudorule_plugin.py b/ipatests/test_xmlrpc/test_sudorule_plugin.py
index 5cdb27f085a..c3ff76796cc 100644
--- a/ipatests/test_xmlrpc/test_sudorule_plugin.py
+++ b/ipatests/test_xmlrpc/test_sudorule_plugin.py
@@ -25,6 +25,7 @@
 import six
 
 from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test, assert_attr_equal
+from ipatests.test_xmlrpc.xmlrpc_test import assert_deepequal
 from ipalib import api
 from ipalib import errors
 
@@ -61,6 +62,7 @@ class test_sudorule(XMLRPC_test):
     test_runasgroup = u'manager'
     test_category = u'all'
     test_option = u'authenticate'
+    test_option2 = u'fqdn'
 
     test_invalid_user = u'+invalid#user'
     test_invalid_host = u'+invalid&host.nonexist.com'
@@ -426,6 +428,29 @@ def test_b_sudorule_remove_option(self):
         )
         assert ret['completed'] == 2
 
+    def test_c_sudorule_add_multiple_options(self):
+        """
+        Test adding two options to Sudo rule using
+        `xmlrpc.sudorule_add_option`.
+        """
+        ret = api.Command['sudorule_add_option'](
+            self.rule_name, ipasudoopt=(self.test_option, self.test_option2)
+        )
+        entry = ret['result']
+        assert_deepequal(entry.get('ipasudoopt'),
+                         (self.test_option, self.test_option2))
+
+    def test_d_sudorule_remove_one_option(self):
+        """
+        Test removing an option from Sudo rule using
+        `xmlrpc.sudorule_remove_option'.
+        """
+        ret = api.Command['sudorule_remove_option'](
+            self.rule_name, ipasudoopt=self.test_option
+        )
+        entry = ret['result']
+        assert_deepequal(entry.get('ipasudoopt'), (self.test_option2,))
+
     def test_a_sudorule_add_host(self):
         """
         Test adding host and hostgroup to Sudo rule using
_______________________________________________
FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org
To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: 
https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org
Do not reply to spam on the list, report it: 
https://pagure.io/fedora-infrastructure

Reply via email to