summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--install/share/schema_compat.uldif1
-rw-r--r--ipalib/plugins/sudorule.py112
-rw-r--r--tests/test_xmlrpc/test_sudorule_plugin.py55
3 files changed, 167 insertions, 1 deletions
diff --git a/install/share/schema_compat.uldif b/install/share/schema_compat.uldif
index bfe645dc5..6f70c1de4 100644
--- a/install/share/schema_compat.uldif
+++ b/install/share/schema_compat.uldif
@@ -87,6 +87,7 @@ add:schema-compat-entry-attribute: 'sudoCommand=%ifeq("cmdCategory","all","ALL",
add:schema-compat-entry-attribute: 'sudoCommand=%ifeq("cmdCategory","all","ALL","!%deref_r(\"memberDenyCmd\",\"member\",\"sudoCmd\")")'
add:schema-compat-entry-attribute: 'sudoRunAsUser=%{ipaSudoRunAsExtUser}'
add:schema-compat-entry-attribute: 'sudoRunAsUser=%deref("ipaSudoRunAs","uid")'
+add:schema-compat-entry-attribute: 'sudoRunAsUser=%ifeq("ipaSudoRunAsUserCategory","all","ALL","%%%deref_f(\"ipaSudoRunAs\",\"(objectclass=posixGroup)\",\"cn\")")'
add:schema-compat-entry-attribute: 'sudoRunAsGroup=%{ipaSudoRunAsExtGroup}'
add:schema-compat-entry-attribute: 'sudoRunAsGroup=%deref("ipaSudoRunAs","cn")'
add:schema-compat-entry-attribute: 'sudoOption=%{ipaSudoOpt}'
diff --git a/ipalib/plugins/sudorule.py b/ipalib/plugins/sudorule.py
index 893f09a57..3361ff5d0 100644
--- a/ipalib/plugins/sudorule.py
+++ b/ipalib/plugins/sudorule.py
@@ -48,7 +48,7 @@ class sudorule(LDAPObject):
'memberhost': ['host', 'hostgroup'],
'memberallowcmd': ['sudocmd', 'sudocmdgroup'],
'memberdenycmd': ['sudocmd', 'sudocmdgroup'],
- 'ipasudorunas': ['user'],
+ 'ipasudorunas': ['user', 'group'],
'ipasudorunasgroup': ['group'],
}
@@ -139,6 +139,16 @@ class sudorule(LDAPObject):
label=_('External User'),
doc=_('External User the rule applies to'),
),
+ Str('ipasudorunasextuser?',
+ cli_name='runasexternaluser',
+ label=_('RunAs External User'),
+ doc=_('External User the commands can run as'),
+ ),
+ Str('ipasudorunasextgroup?',
+ cli_name='runasexternalgroup',
+ label=_('RunAs External Group'),
+ doc=_('External Group the commands can run as'),
+ ),
)
api.register(sudorule)
@@ -429,6 +439,32 @@ class sudorule_add_runasuser(LDAPAddMember):
member_attributes = ['ipasudorunas']
member_count_out = ('%i object added.', '%i objects added.')
+ def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ completed_external = 0
+ # Sift through the user failures. We assume that these are all
+ # users that aren't stored in IPA, aka external users.
+ if 'ipasudorunas' in failed and 'user' in failed['ipasudorunas']:
+ (dn, entry_attrs_) = ldap.get_entry(dn, ['ipasudorunasextuser'])
+ members = entry_attrs.get('ipasudorunas', [])
+ external_users = entry_attrs_.get('ipasudorunasextuser', [])
+ failed_users = []
+ for user in failed['ipasudorunas']['user']:
+ username = user[0].lower()
+ user_dn = self.api.Object['user'].get_dn(username)
+ if username not in external_users and user_dn not in members:
+ external_users.append(username)
+ completed_external += 1
+ else:
+ failed_users.append(username)
+ if completed_external:
+ try:
+ ldap.update_entry(dn, {'ipasudorunasextuser': external_users})
+ except errors.EmptyModlist:
+ pass
+ failed['ipasudorunas']['user'] = failed_users
+ entry_attrs['ipasudorunasextuser'] = external_users
+ return (completed + completed_external, dn)
+
api.register(sudorule_add_runasuser)
@@ -439,6 +475,30 @@ class sudorule_remove_runasuser(LDAPRemoveMember):
member_attributes = ['ipasudorunas']
member_count_out = ('%i object removed.', '%i objects removed.')
+ def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ # Run through the user failures and gracefully remove any defined as
+ # as an externaluser.
+ if 'ipasudorunas' in failed and 'user' in failed['ipasudorunas']:
+ (dn, entry_attrs) = ldap.get_entry(dn, ['ipasudorunasextuser'])
+ external_users = entry_attrs.get('ipasudorunasextuser', [])
+ failed_users = []
+ completed_external = 0
+ for user in failed['ipasudorunas']['user']:
+ username = user[0].lower()
+ if username in external_users:
+ external_users.remove(username)
+ completed_external += 1
+ else:
+ failed_users.append(username)
+ if completed_external:
+ try:
+ ldap.update_entry(dn, {'ipasudorunasextuser': external_users})
+ except errors.EmptyModlist:
+ pass
+ failed['ipasudorunas']['user'] = failed_users
+ entry_attrs['ipasudorunasextuser'] = external_users
+ return (completed + completed_external, dn)
+
api.register(sudorule_remove_runasuser)
@@ -449,6 +509,32 @@ class sudorule_add_runasgroup(LDAPAddMember):
member_attributes = ['ipasudorunasgroup']
member_count_out = ('%i object added.', '%i objects added.')
+ def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ completed_external = 0
+ # Sift through the group failures. We assume that these are all
+ # groups that aren't stored in IPA, aka external groups.
+ if 'ipasudorunasgroup' in failed and 'group' in failed['ipasudorunasgroup']:
+ (dn, entry_attrs_) = ldap.get_entry(dn, ['ipasudorunasextgroup'])
+ members = entry_attrs.get('ipasudorunasgroup', [])
+ external_groups = entry_attrs_.get('ipasudorunasextgroup', [])
+ failed_groups = []
+ for group in failed['ipasudorunasgroup']['group']:
+ groupname = group[0].lower()
+ group_dn = self.api.Object['group'].get_dn(groupname)
+ if groupname not in external_groups and group_dn not in members:
+ external_groups.append(groupname)
+ completed_external += 1
+ else:
+ failed_groups.append(groupname)
+ if completed_external:
+ try:
+ ldap.update_entry(dn, {'ipasudorunasextgroup': external_groups})
+ except errors.EmptyModlist:
+ pass
+ failed['ipasudorunasgroup']['group'] = failed_groups
+ entry_attrs['ipasudorunasextgroup'] = external_groups
+ return (completed + completed_external, dn)
+
api.register(sudorule_add_runasgroup)
@@ -459,6 +545,30 @@ class sudorule_remove_runasgroup(LDAPRemoveMember):
member_attributes = ['ipasudorunasgroup']
member_count_out = ('%i object removed.', '%i objects removed.')
+ def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ # Run through the group failures and gracefully remove any defined as
+ # as an external group.
+ if 'ipasudorunasgroup' in failed and 'group' in failed['ipasudorunasgroup']:
+ (dn, entry_attrs) = ldap.get_entry(dn, ['ipasudorunasextgroup'])
+ external_groups = entry_attrs.get('ipasudorunasextgroup', [])
+ failed_groups = []
+ completed_external = 0
+ for group in failed['ipasudorunasgroup']['group']:
+ groupname = group[0].lower()
+ if groupname in external_groups:
+ external_groups.remove(groupname)
+ completed_external += 1
+ else:
+ failed_groups.append(groupname)
+ if completed_external:
+ try:
+ ldap.update_entry(dn, {'ipasudorunasextgroup': external_groups})
+ except errors.EmptyModlist:
+ pass
+ failed['ipasudorunasgroup']['group'] = failed_groups
+ entry_attrs['ipasudorunasextgroup'] = external_groups
+ return (completed + completed_external, dn)
+
api.register(sudorule_remove_runasgroup)
diff --git a/tests/test_xmlrpc/test_sudorule_plugin.py b/tests/test_xmlrpc/test_sudorule_plugin.py
index 89f0af3a5..4c3ba5a21 100644
--- a/tests/test_xmlrpc/test_sudorule_plugin.py
+++ b/tests/test_xmlrpc/test_sudorule_plugin.py
@@ -38,6 +38,7 @@ class test_sudorule(XMLRPC_test):
test_user = u'sudorule_test_user'
test_external_user = u'external_test_user'
test_group = u'sudorule_test_group'
+ test_external_group = u'external_test_group'
test_host = u'sudorule._test_host'
test_external_host = u'external._test_host'
test_hostgroup = u'sudorule_test_hostgroup'
@@ -264,6 +265,60 @@ class test_sudorule(XMLRPC_test):
entry = ret['result']
assert 'externaluser' not in entry
+ def test_a_sudorule_add_runasexternaluser(self):
+ """
+ Test adding an external runasuser to Sudo rule using
+ `xmlrpc.sudorule_add_runasuser`.
+ """
+ ret = api.Command['sudorule_add_runasuser'](
+ self.rule_name, user=self.test_external_user
+ )
+ assert ret['completed'] == 1
+ failed = ret['failed']
+ entry = ret['result']
+ assert_attr_equal(entry, 'ipasudorunasextuser', self.test_external_user)
+
+ def test_b_sudorule_remove_runasexternaluser(self):
+ """
+ Test removing an external runasuser from Sudo rule using
+ `xmlrpc.sudorule_remove_runasuser'.
+ """
+ ret = api.Command['sudorule_remove_runasuser'](
+ self.rule_name, user=self.test_external_user
+ )
+ assert ret['completed'] == 1
+ failed = ret['failed']
+ entry = ret['result']
+ assert 'ipasudorunasextuser' not in entry
+
+ def test_a_sudorule_add_runasexternalgroup(self):
+ """
+ Test adding an external runasgroup to Sudo rule using
+ `xmlrpc.sudorule_add_runasgroup`.
+ """
+ ret = api.Command['sudorule_add_runasgroup'](
+ self.rule_name, group=self.test_external_group
+ )
+ print ret
+ assert ret['completed'] == 1
+ failed = ret['failed']
+ entry = ret['result']
+ assert_attr_equal(entry, 'ipasudorunasextgroup', self.test_external_group)
+
+ def test_b_sudorule_remove_runasexternalgroup(self):
+ """
+ Test removing an external runasgroup from Sudo rule using
+ `xmlrpc.sudorule_remove_runasgroup'.
+ """
+ ret = api.Command['sudorule_remove_runasgroup'](
+ self.rule_name, group=self.test_external_group
+ )
+ print ret
+ assert ret['completed'] == 1
+ failed = ret['failed']
+ entry = ret['result']
+ assert 'ipasudorunasextgroup' not in entry
+
def test_a_sudorule_add_option(self):
"""
Test adding an option to Sudo rule using