permission plugin: Write support for extratargetfilter

Extend the permission-add and permission-mod commands to process
extratargetfilter.

Part of the work for: https://fedorahosted.org/freeipa/ticket/4216

Reviewed-By: Martin Kosek <mkosek@redhat.com>
This commit is contained in:
Petr Viktorin 2014-03-04 16:59:33 +01:00 committed by Martin Kosek
parent 3120a6833e
commit f58ffe176c

View File

@ -285,6 +285,61 @@ class permission(baseldap.LDAPObject):
raise errors.ACIError( raise errors.ACIError(
info=_('A SYSTEM permission may not be modified or removed')) info=_('A SYSTEM permission may not be modified or removed'))
def _get_filter_attr_info(self, entry):
"""Get information on filter-related virtual attributes
Returns a dict with this information:
'implicit_targetfilters': targetfilters implied by memberof and type
'memberof': list of names of groups from memberof
'type': the type
"""
ipapermtargetfilter = entry.get('ipapermtargetfilter', [])
ipapermlocation = entry.single_value.get('ipapermlocation')
implicit_targetfilters = set()
result = {'implicit_targetfilters': implicit_targetfilters}
# memberof
memberof = []
for targetfilter in ipapermtargetfilter:
match = re.match('^\(memberof=(.*)\)$', targetfilter, re.I)
if match:
dn = DN(match.group(1))
groups_dn = DN(self.api.Object.group.container_dn,
self.api.env.basedn)
if dn[1:] == groups_dn[:] and dn[0].attr == 'cn':
memberof.append(dn[0].value)
implicit_targetfilters.add(match.group(0))
if memberof:
result['memberof'] = memberof
# type
if ipapermtargetfilter and ipapermlocation:
for obj in self.api.Object():
filter_objectclasses = getattr(
obj, 'permission_filter_objectclasses', None)
if not filter_objectclasses:
continue
wantdn = DN(obj.container_dn, self.api.env.basedn)
if DN(ipapermlocation) != wantdn:
continue
objectclass_targetfilters = set()
for objclass in filter_objectclasses:
filter_re = '\(objectclass=%s\)' % re.escape(objclass)
for tf in ipapermtargetfilter:
if re.match(filter_re, tf, re.I):
objectclass_targetfilters.add(tf)
break
else:
break
else:
result['type'] = [unicode(obj.name)]
implicit_targetfilters |= objectclass_targetfilters
break
return result
def postprocess_result(self, entry, options): def postprocess_result(self, entry, options):
"""Update a permission entry for output (in place) """Update a permission entry for output (in place)
@ -299,23 +354,6 @@ class permission(baseldap.LDAPObject):
if not options.get('raw') and not options.get('pkey_only'): if not options.get('raw') and not options.get('pkey_only'):
ipapermtargetfilter = entry.get('ipapermtargetfilter', []) ipapermtargetfilter = entry.get('ipapermtargetfilter', [])
ipapermtarget = entry.single_value.get('ipapermtarget') ipapermtarget = entry.single_value.get('ipapermtarget')
ipapermlocation = entry.single_value.get('ipapermlocation')
implicit_targetfilters = set()
# memberof
memberof = []
for targetfilter in ipapermtargetfilter:
match = re.match('^\(memberof=(.*)\)$', targetfilter, re.I)
if match:
dn = DN(match.group(1))
groups_dn = DN(self.api.Object.group.container_dn,
self.api.env.basedn)
if dn[1:] == groups_dn[:] and dn[0].attr == 'cn':
memberof.append(dn[0].value)
implicit_targetfilters.add(match.group(0))
if memberof:
entry['memberof'] = memberof
# targetgroup # targetgroup
if ipapermtarget: if ipapermtarget:
@ -325,34 +363,15 @@ class permission(baseldap.LDAPObject):
dn[0].attr == 'cn' and dn[0].value != '*'): dn[0].attr == 'cn' and dn[0].value != '*'):
entry.single_value['targetgroup'] = dn[0].value entry.single_value['targetgroup'] = dn[0].value
# type filter_attr_info = self._get_filter_attr_info(entry)
if ipapermtargetfilter and ipapermlocation: if 'type' in filter_attr_info:
for obj in self.api.Object(): entry['type'] = filter_attr_info['type']
filter_objectclasses = getattr( if 'memberof' in filter_attr_info:
obj, 'permission_filter_objectclasses', None) entry['memberof'] = filter_attr_info['memberof']
if not filter_objectclasses: if 'implicit_targetfilters' in filter_attr_info:
continue
wantdn = DN(obj.container_dn, self.api.env.basedn)
if DN(ipapermlocation) != wantdn:
continue
objectclass_targetfilters = set()
for objclass in filter_objectclasses:
filter_re = '\(objectclass=%s\)' % re.escape(objclass)
for tf in ipapermtargetfilter:
if re.match(filter_re, tf, re.I):
objectclass_targetfilters.add(tf)
break
else:
break
else:
entry.single_value['type'] = unicode(obj.name)
implicit_targetfilters |= objectclass_targetfilters
break
if ipapermtargetfilter:
extratargetfilter = sorted( extratargetfilter = sorted(
set(ipapermtargetfilter) - implicit_targetfilters) set(ipapermtargetfilter) -
filter_attr_info['implicit_targetfilters'])
if extratargetfilter: if extratargetfilter:
entry['extratargetfilter'] = extratargetfilter entry['extratargetfilter'] = extratargetfilter
@ -652,7 +671,9 @@ class permission(baseldap.LDAPObject):
raise ValueError('Cannot convert ACI, %r != %r' % (new_acistring, raise ValueError('Cannot convert ACI, %r != %r' % (new_acistring,
acistring)) acistring))
def preprocess_options(self, options, return_filter_ops=False): def preprocess_options(self, options,
return_filter_ops=False,
merge_targetfilter=False):
"""Preprocess options (in-place) """Preprocess options (in-place)
:param options: A dictionary of options :param options: A dictionary of options
@ -667,8 +688,19 @@ class permission(baseldap.LDAPObject):
- remove: list of regular expression objects; values that match - remove: list of regular expression objects; values that match
any of them sould be removed any of them sould be removed
- add: list of values to be added, after any removals - add: list of values to be added, after any removals
:merge_targetfilter:
If true, the extratargetfilter is copied into ipapermtargetfilter.
""" """
if 'extratargetfilter' in options:
if 'ipapermtargetfilter' in options:
raise errors.ValidationError(
name='ipapermtargetfilter',
error=_('cannot specify full target filter '
'and extra target filter simultaneously'))
if merge_targetfilter:
options['ipapermtargetfilter'] = options['extratargetfilter']
filter_ops = {'add': [], 'remove': []} filter_ops = {'add': [], 'remove': []}
if options.get('subtree'): if options.get('subtree'):
@ -836,7 +868,7 @@ class permission_add(baseldap.LDAPCreate):
# Need to override execute so that processed options apply to # Need to override execute so that processed options apply to
# the whole command, not just the callbacks # the whole command, not just the callbacks
def execute(self, *keys, **options): def execute(self, *keys, **options):
self.obj.preprocess_options(options) self.obj.preprocess_options(options, merge_targetfilter=True)
return super(permission_add, self).execute(*keys, **options) return super(permission_add, self).execute(*keys, **options)
def pre_callback(self, ldap, dn, entry, attrs_list, *keys, **options): def pre_callback(self, ldap, dn, entry, attrs_list, *keys, **options):
@ -950,7 +982,8 @@ class permission_mod(baseldap.LDAPUpdate):
error=_('cannot rename managed permissions')) error=_('cannot rename managed permissions'))
option = self.options[option_name] option = self.options[option_name]
allow_mod = 'allow_mod_for_managed_permission' in option.flags allow_mod = 'allow_mod_for_managed_permission' in option.flags
if option.attribute and not allow_mod: if (option.attribute and not allow_mod or
option_name == 'extratargetfilter'):
raise errors.ValidationError( raise errors.ValidationError(
name=option_name, name=option_name,
error=_('not modifiable on managed permissions')) error=_('not modifiable on managed permissions'))
@ -994,6 +1027,14 @@ class permission_mod(baseldap.LDAPUpdate):
key not in self.obj.attribute_members): key not in self.obj.attribute_members):
entry.setdefault(key, value) entry.setdefault(key, value)
# For extratargetfilter, add it to the implicit filters
# to get the full target filter
if 'extratargetfilter' in options:
filter_attr_info = self.obj._get_filter_attr_info(entry)
entry['ipapermtargetfilter'] = (
list(options['extratargetfilter'] or []) +
list(filter_attr_info['implicit_targetfilters']))
filter_ops = context.filter_ops filter_ops = context.filter_ops
removes = filter_ops.get('remove', []) removes = filter_ops.get('remove', [])
new_filters = set( new_filters = set(