diff options
-rw-r--r-- | API.txt | 6 | ||||
-rw-r--r-- | install/share/schema_compat.uldif | 1 | ||||
-rw-r--r-- | install/updates/10-schema_compat.update | 3 | ||||
-rw-r--r-- | ipalib/plugins/sudorule.py | 73 | ||||
-rw-r--r-- | ipalib/util.py | 7 |
5 files changed, 86 insertions, 4 deletions
@@ -3474,11 +3474,12 @@ output: Output('completed', <type 'int'>, None) output: Output('failed', <type 'dict'>, None) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: sudorule_add_host -args: 1,6,3 +args: 1,7,3 arg: Str('cn', attribute=True, cli_name='sudorule_name', multivalue=False, primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('host*', alwaysask=True, cli_name='hosts', csv=True) option: Str('hostgroup*', alwaysask=True, cli_name='hostgroups', csv=True) +option: Str('hostmask?', multivalue=True) option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Str('version?', exclude='webui') @@ -3627,11 +3628,12 @@ output: Output('completed', <type 'int'>, None) output: Output('failed', <type 'dict'>, None) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) command: sudorule_remove_host -args: 1,6,3 +args: 1,7,3 arg: Str('cn', attribute=True, cli_name='sudorule_name', multivalue=False, primary_key=True, query=True, required=True) option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') option: Str('host*', alwaysask=True, cli_name='hosts', csv=True) option: Str('hostgroup*', alwaysask=True, cli_name='hostgroups', csv=True) +option: Str('hostmask?', multivalue=True) option: Flag('no_members', autofill=True, default=False, exclude='webui') option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') option: Str('version?', exclude='webui') diff --git a/install/share/schema_compat.uldif b/install/share/schema_compat.uldif index 9a9607eeb..3d2681d64 100644 --- a/install/share/schema_compat.uldif +++ b/install/share/schema_compat.uldif @@ -86,6 +86,7 @@ add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","% add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","%deref_rf(\"memberHost\",\"(&(objectclass=ipaHostGroup)(!(objectclass=mepOriginEntry)))\",\"member\",\"(|(objectclass=ipaHostGroup)(objectclass=ipaHost))\",\"fqdn\")")' add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","+%deref_f(\"memberHost\",\"(&(objectclass=ipaHostGroup)(objectclass=mepOriginEntry))\",\"cn\")")' add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","+%deref_f(\"memberHost\",\"(objectclass=ipaNisNetgroup)\",\"cn\")")' +add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","%{hostMask}")' add:schema-compat-entry-attribute: 'sudoCommand=%ifeq("cmdCategory","all","ALL","%deref(\"memberAllowCmd\",\"sudoCmd\")")' add:schema-compat-entry-attribute: 'sudoCommand=%ifeq("cmdCategory","all","ALL","%deref_r(\"memberAllowCmd\",\"member\",\"sudoCmd\")")' add:schema-compat-entry-attribute: 'sudoCommand=!%deref("memberDenyCmd","sudoCmd")' diff --git a/install/updates/10-schema_compat.update b/install/updates/10-schema_compat.update index 505bfcaa8..c45734c55 100644 --- a/install/updates/10-schema_compat.update +++ b/install/updates/10-schema_compat.update @@ -2,6 +2,9 @@ dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config only:schema-compat-entry-rdn:'%ifeq("ipaEnabledFlag", "FALSE", "DISABLED", "cn=%{cn}")' replace: schema-compat-entry-attribute:'sudoRunAsGroup=%deref("ipaSudoRunAs","cn")::sudoRunAsGroup=%deref_f("ipaSudoRunAsGroup","(objectclass=posixGroup)","cn")' +dn: cn=sudoers,cn=Schema Compatibility,cn=plugins,cn=config +add:schema-compat-entry-attribute: 'sudoHost=%ifeq("hostCategory","all","ALL","%{hostMask}")' + # Change padding for host and userCategory so the pad returns the same value # as the original, '' or -. dn: cn=ng,cn=Schema Compatibility,cn=plugins,cn=config diff --git a/ipalib/plugins/sudorule.py b/ipalib/plugins/sudorule.py index 87242ead3..a304373b3 100644 --- a/ipalib/plugins/sudorule.py +++ b/ipalib/plugins/sudorule.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import netaddr + from ipalib import api, errors from ipalib import Str, StrEnum, Bool, Int from ipalib.plugable import Registry @@ -30,6 +32,7 @@ from ipalib.plugins.baseldap import (LDAPObject, LDAPCreate, LDAPDelete, external_host_param) from ipalib.plugins.hbacrule import is_all from ipalib import _, ngettext +from ipalib.util import validate_hostmask from ipapython.dn import DN __doc__ = _(""" @@ -94,6 +97,12 @@ def deprecated(attribute): error=_('this option has been deprecated.')) +hostmask_membership_param = Str('hostmask?', validate_hostmask, + label=_('host masks of allowed hosts'), + flags=['no_create', 'no_update', 'no_search'], + multivalue=True, + ) + def validate_externaluser(ugettext, value): deprecated('externaluser') @@ -123,7 +132,7 @@ class sudorule(LDAPObject): 'memberallowcmd', 'memberdenycmd', 'ipasudoopt', 'ipasudorunas', 'ipasudorunasgroup', 'ipasudorunasusercategory', 'ipasudorunasgroupcategory', - 'sudoorder', + 'sudoorder', 'hostmask', ] uuid_attribute = 'ipauniqueid' rdn_attribute = 'ipauniqueid' @@ -267,6 +276,12 @@ class sudorule(LDAPObject): label=_('Host Groups'), flags=['no_create', 'no_update', 'no_search'], ), + Str('hostmask', validate_hostmask, + normalizer=lambda x: unicode(netaddr.IPNetwork(x).cidr), + label=_('Host Masks'), + flags=['no_create', 'no_update', 'no_search'], + multivalue=True, + ), Str('memberallowcmd_sudocmd?', label=_('Sudo Allow Commands'), flags=['no_create', 'no_update', 'no_search'], @@ -577,6 +592,11 @@ class sudorule_add_host(LDAPAddMember): member_attributes = ['memberhost'] member_count_out = ('%i object added.', '%i objects added.') + def get_options(self): + for option in super(sudorule_add_host, self).get_options(): + yield option + yield hostmask_membership_param + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): assert isinstance(dn, DN) try: @@ -593,7 +613,30 @@ class sudorule_add_host(LDAPAddMember): def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) - return add_external_post_callback('memberhost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, options) + try: + _entry_attrs = ldap.get_entry(dn, self.obj.default_attributes) + except errors.NotFound: + self.obj.handle_not_found(*keys) + + if 'hostmask' in options: + norm = lambda x: unicode(netaddr.IPNetwork(x).cidr) + + old_masks = set(map(norm, _entry_attrs.get('hostmask', []))) + new_masks = set(map(norm, options['hostmask'])) + + num_added = len(new_masks - old_masks) + + if num_added: + entry_attrs['hostmask'] = list(old_masks | new_masks) + try: + ldap.update_entry(entry_attrs) + except errors.EmptyModlist: + pass + completed = completed + num_added + + return add_external_post_callback('memberhost', 'host', 'externalhost', + ldap, completed, failed, dn, + entry_attrs, keys, options) @@ -604,9 +647,35 @@ class sudorule_remove_host(LDAPRemoveMember): member_attributes = ['memberhost'] member_count_out = ('%i object removed.', '%i objects removed.') + def get_options(self): + for option in super(sudorule_remove_host, self).get_options(): + yield option + yield hostmask_membership_param def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) + + try: + _entry_attrs = ldap.get_entry(dn, self.obj.default_attributes) + except errors.NotFound: + self.obj.handle_not_found(*keys) + + if 'hostmask' in options: + norm = lambda x: unicode(netaddr.IPNetwork(x).cidr) + + old_masks = set(map(norm, _entry_attrs.get('hostmask', []))) + removed_masks = set(map(norm, options['hostmask'])) + + num_added = len(removed_masks & old_masks) + + if num_added: + entry_attrs['hostmask'] = list(old_masks - removed_masks) + try: + ldap.update_entry(entry_attrs) + except errors.EmptyModlist: + pass + completed = completed + num_added + return remove_external_post_callback('memberhost', 'host', 'externalhost', ldap, completed, failed, dn, entry_attrs, keys, diff --git a/ipalib/util.py b/ipalib/util.py index 265957949..ef759d8d1 100644 --- a/ipalib/util.py +++ b/ipalib/util.py @@ -32,6 +32,7 @@ from types import NoneType from weakref import WeakKeyDictionary from dns import resolver, rdatatype from dns.exception import DNSException +from netaddr.core import AddrFormatError from ipalib import errors from ipalib.text import _ @@ -544,3 +545,9 @@ def validate_rdn_param(ugettext, value): except Exception, e: return str(e) return None + +def validate_hostmask(ugettext, hostmask): + try: + netaddr.IPNetwork(hostmask) + except (ValueError, AddrFormatError): + return _('invalid hostmask') |