summaryrefslogtreecommitdiffstats
path: root/ipalib/plugins/permission.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib/plugins/permission.py')
-rw-r--r--ipalib/plugins/permission.py144
1 files changed, 131 insertions, 13 deletions
diff --git a/ipalib/plugins/permission.py b/ipalib/plugins/permission.py
index 875a9f5b1..d003bcabb 100644
--- a/ipalib/plugins/permission.py
+++ b/ipalib/plugins/permission.py
@@ -79,6 +79,15 @@ Setting one of these options will set the corresponding attribute(s).
3. targetgroup: grant access to modify a specific group (such as granting
the rights to manage group membership); sets target.
""") + _("""
+Managed permissions
+""") + _("""
+Permissions that come with IPA by default can be so-called "managed"
+permissions. These have a default set of attributes they apply to,
+but the administrator can add/remove individual attributes to/from the set.
+""") + _("""
+Deleting or renaming a managed permission, as well as changing its target,
+is not allowed.
+""") + _("""
EXAMPLES:
""") + _("""
Add a permission that grants the creation of users:
@@ -95,12 +104,11 @@ VALID_OBJECT_TYPES = (u'user', u'group', u'host', u'service', u'hostgroup',
_DEPRECATED_OPTION_ALIASES = {
'permissions': 'ipapermright',
- 'attrs': 'ipapermallowedattr',
'filter': 'ipapermtargetfilter',
'subtree': 'ipapermlocation',
}
-KNOWN_FLAGS = {'SYSTEM', 'V2'}
+KNOWN_FLAGS = {'SYSTEM', 'V2', 'MANAGED'}
output_params = (
Str('aci',
@@ -139,7 +147,7 @@ class permission(baseldap.LDAPObject):
object_class = ['groupofnames', 'ipapermission', 'ipapermissionv2']
default_attributes = ['cn', 'member', 'memberof',
'memberindirect', 'ipapermissiontype', 'objectclass',
- 'ipapermdefaultattr', 'ipapermallowedattr', 'ipapermexcludedattr',
+ 'ipapermdefaultattr', 'ipapermincludedattr', 'ipapermexcludedattr',
'ipapermbindruletype', 'ipapermlocation', 'ipapermright',
'ipapermtargetfilter', 'ipapermtarget'
]
@@ -169,10 +177,29 @@ class permission(baseldap.LDAPObject):
values=(u'read', u'search', u'compare',
u'write', u'add', u'delete', u'all'),
),
- Str('ipapermallowedattr*',
- cli_name='attrs',
- label=_('Attributes'),
- doc=_('Attributes to which the permission applies'),
+ Str('attrs*',
+ label=_('Effective attributes'),
+ doc=_('All attributes to which the permission applies'),
+ flags={'virtual_attribute', 'allow_mod_for_managed_permission'},
+ ),
+ Str('ipapermincludedattr*',
+ cli_name='includedattrs',
+ label=_('Included attributes'),
+ doc=_('User-specified attributes to which the permission applies'),
+ flags={'no_create', 'allow_mod_for_managed_permission'},
+ ),
+ Str('ipapermexcludedattr*',
+ cli_name='excludedattrs',
+ label=_('Excluded attributes'),
+ doc=_('User-specified attributes to which the permission '
+ 'explicitly does not apply'),
+ flags={'no_create', 'allow_mod_for_managed_permission'},
+ ),
+ Str('ipapermdefaultattr*',
+ cli_name='defaultattrs',
+ label=_('Default attributes'),
+ doc=_('Attributes to which the permission applies by default'),
+ flags={'no_create', 'no_update'},
),
StrEnum(
'ipapermbindruletype',
@@ -182,6 +209,7 @@ class permission(baseldap.LDAPObject):
autofill=True,
values=(u'permission', u'all', u'anonymous'),
default=u'permission',
+ flags={'allow_mod_for_managed_permission'},
),
DNOrURL(
'ipapermlocation?',
@@ -301,6 +329,12 @@ class permission(baseldap.LDAPObject):
rights['type'] = ''.join(sorted(type_rights,
key=rights['ipapermtarget'].index))
+ if 'ipapermincludedattr' in rights:
+ rights['attrs'] = ''.join(sorted(
+ set(rights['ipapermincludedattr']) &
+ set(rights.get('ipapermexcludedattr', '')),
+ key=rights['ipapermincludedattr'].index))
+
if not client_has_capability(options['version'], 'permissions2'):
for old_name, new_name in _DEPRECATED_OPTION_ALIASES.items():
if new_name in entry:
@@ -319,6 +353,14 @@ class permission(baseldap.LDAPObject):
raise
else:
entry.single_value['aci'] = acistring
+ else:
+ effective_attrs = self.get_effective_attrs(entry)
+ if effective_attrs:
+ entry['attrs'] = effective_attrs
+ if (not options.get('all') and
+ not entry.get('ipapermexcludedattr') and
+ not entry.get('ipapermdefaultattr')):
+ entry.pop('ipapermincludedattr', None)
if not client_has_capability(options['version'], 'permissions2'):
# Legacy clients expect some attributes as a single value
@@ -337,6 +379,12 @@ class permission(baseldap.LDAPObject):
new_filter.append(flt[1:-1])
entry['filter'] = new_filter
+ def get_effective_attrs(self, entry):
+ attrs = set(entry.get('ipapermdefaultattr', ()))
+ attrs.update(entry.get('ipapermincludedattr', ()))
+ attrs.difference_update(entry.get('ipapermexcludedattr', ()))
+ return sorted(attrs)
+
def make_aci(self, entry):
"""Make an ACI string from the given permission entry"""
@@ -344,7 +392,7 @@ class permission(baseldap.LDAPObject):
name = entry.single_value['cn']
# targetattr
- attrs = entry.get('ipapermallowedattr', [])
+ attrs = self.get_effective_attrs(entry)
if attrs:
aci_parts.append("(targetattr = \"%s\")" % ' || '.join(attrs))
@@ -502,9 +550,6 @@ class permission(baseldap.LDAPObject):
aci = ACI(acistring)
- if 'targetattr' in aci.target:
- target_entry['ipapermallowedattr'] = (
- aci.target['targetattr']['expression'])
if 'target' in aci.target:
target_entry.single_value['ipapermtarget'] = DN(strip_ldap_prefix(
aci.target['target']['expression']))
@@ -519,7 +564,7 @@ class permission(baseldap.LDAPObject):
target_entry.single_value['ipapermbindruletype'] = u'permission'
target_entry['ipapermright'] = aci.permissions
if 'targetattr' in aci.target:
- target_entry['ipapermallowedattr'] = [
+ target_entry['ipapermincludedattr'] = [
unicode(a) for a in aci.target['targetattr']['expression']]
if not output_only:
@@ -655,7 +700,8 @@ class permission(baseldap.LDAPObject):
# Ensure there's something in the ACI's filter
needed_attrs = (
- 'ipapermtarget', 'ipapermtargetfilter', 'ipapermallowedattr')
+ 'ipapermtarget', 'ipapermtargetfilter',
+ 'ipapermincludedattr', 'ipapermexcludedattr', 'ipapermdefaultattr')
if not any(entry.single_value.get(a) for a in needed_attrs):
raise errors.ValidationError(
name='target',
@@ -717,6 +763,14 @@ class permission_add(baseldap.LDAPCreate):
if not entry.get('ipapermlocation'):
entry.setdefault('ipapermlocation', [api.env.basedn])
+ if 'attrs' in options:
+ if 'ipapermincludedattr' in options:
+ raise errors.ValidationError(
+ name='attrs',
+ error=_('attrs and included attributes are '
+ 'mutually exclusive'))
+ entry['ipapermincludedattr'] = list(options.pop('attrs') or ())
+
self.obj.validate_permission(entry)
return dn
@@ -748,6 +802,9 @@ class permission_del(baseldap.LDAPDelete):
if not options.get('force'):
self.obj.reject_system(entry)
+ if entry.get('ipapermdefaultattr'):
+ raise errors.ACIError(
+ info=_('cannot delete managed permissions'))
try:
self.obj.remove_aci(entry)
@@ -783,6 +840,38 @@ class permission_mod(baseldap.LDAPUpdate):
self.obj.reject_system(old_entry)
self.obj.upgrade_permission(old_entry)
+ if 'MANAGED' in old_entry.get('ipapermissiontype', ()):
+ for option_name in sorted(options):
+ if option_name == 'rename':
+ raise errors.ValidationError(
+ name=option_name,
+ error=_('cannot rename managed permissions'))
+ option = self.options[option_name]
+ allow_mod = 'allow_mod_for_managed_permission' in option.flags
+ if option.attribute and not allow_mod:
+ raise errors.ValidationError(
+ name=option_name,
+ error=_('not modifiable on managed permissions'))
+ else:
+ if options.get('ipapermexcludedattr'):
+ # prevent setting excluded attributes on normal permissions
+ # (but do allow deleting them all)
+ raise errors.ValidationError(
+ name='ipapermexcludedattr',
+ error=_('only available on managed permissions'))
+
+ if 'attrs' in options:
+ if any(a in options for a in ('ipapermincludedattr',
+ 'ipapermexcludedattr')):
+ raise errors.ValidationError(
+ name='attrs',
+ error=_('attrs and included/excluded attributes are '
+ 'mutually exclusive'))
+ attrs = set(options.pop('attrs') or ())
+ defaults = set(old_entry.get('ipapermdefaultattr', ()))
+ entry['ipapermincludedattr'] = list(attrs - defaults)
+ entry['ipapermexcludedattr'] = list(defaults - attrs)
+
# Check setting bindtype for an assigned permission
if options.get('ipapermbindruletype') and old_entry.get('member'):
raise errors.ValidationError(
@@ -866,7 +955,36 @@ class permission_find(baseldap.LDAPSearch):
self.obj.preprocess_options(options)
return super(permission_find, self).execute(*keys, **options)
+ def pre_callback(self, ldap, filters, attrs_list, base_dn, scope,
+ *args, **options):
+ if 'attrs' in options and 'ipapermincludedattr' in options:
+ raise errors.ValidationError(
+ name='attrs',
+ error=_('attrs and included/excluded attributes are '
+ 'mutually exclusive'))
+
+ if options.get('attrs'):
+ # Effective attributes:
+ # each attr must be in either default or included,
+ # but not in excluded
+ filters = ldap.combine_filters(
+ [filters] + [
+ '(&'
+ '(|'
+ '(ipapermdefaultattr=%(attr)s)'
+ '(ipapermincludedattr=%(attr)s))'
+ '(!(ipapermexcludedattr=%(attr)s)))' % {'attr': attr}
+ for attr in options['attrs']
+ ],
+ ldap.MATCH_ALL,
+ )
+
+ return filters, base_dn, scope
+
def post_callback(self, ldap, entries, truncated, *args, **options):
+ if 'attrs' in options:
+ options['ipapermincludedattr'] = options['attrs']
+
attribute_options = [o for o in options
if (o in self.options and
self.options[o].attribute)]