summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Viktorin <pviktori@redhat.com>2013-09-19 17:41:04 +0200
committerPetr Viktorin <pviktori@redhat.com>2014-03-25 14:18:12 +0100
commitf4de4a2aa75db990c787b7a8dbc6b3bdd7e01a06 (patch)
tree02d799adcab253b8d0b6c6a629283ca01150bbcc
parent1df9b5836ad26bab3513b726305f5e061424e2c9 (diff)
Add Object metadata and update plugin for managed permissions
The default read permission is added for Netgroup as an example. Part of the work for: https://fedorahosted.org/freeipa/ticket/3566 Design: http://www.freeipa.org/page/V3/Managed_Read_permissions Reviewed-By: Martin Kosek <mkosek@redhat.com>
-rw-r--r--ipalib/plugins/baseldap.py1
-rw-r--r--ipalib/plugins/netgroup.py19
-rw-r--r--ipaserver/install/plugins/update_managed_permissions.py160
3 files changed, 180 insertions, 0 deletions
diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py
index c4951eb56..6a8b4f822 100644
--- a/ipalib/plugins/baseldap.py
+++ b/ipalib/plugins/baseldap.py
@@ -469,6 +469,7 @@ class LDAPObject(Object):
}
label = _('Entry')
label_singular = _('Entry')
+ managed_permissions = {}
container_not_found_msg = _('container entry (%(container)s) not found')
parent_not_found_msg = _('%(parent)s: %(oname)s not found')
diff --git a/ipalib/plugins/netgroup.py b/ipalib/plugins/netgroup.py
index fe27e6cb6..7136c18f9 100644
--- a/ipalib/plugins/netgroup.py
+++ b/ipalib/plugins/netgroup.py
@@ -105,6 +105,25 @@ class netgroup(LDAPObject):
'memberuser': ('Member', '', 'no_'),
'memberhost': ('Member', '', 'no_'),
}
+ managed_permissions = {
+ 'System: Read Netgroups': {
+ 'replaces_global_anonymous_aci': True,
+ 'ipapermbindruletype': 'all',
+ 'ipapermright': {'read', 'search', 'compare'},
+ 'ipapermdefaultattr': {
+ 'cn', 'description', 'hostcategory', 'ipaenabledflag',
+ 'ipauniqueid', 'nisdomainname', 'usercategory'
+ },
+ },
+ 'System: Read Netgroup Membership': {
+ 'replaces_global_anonymous_aci': True,
+ 'ipapermbindruletype': 'all',
+ 'ipapermright': {'read', 'search', 'compare'},
+ 'ipapermdefaultattr': {
+ 'externalhost', 'member', 'memberof', 'memberuser'
+ },
+ },
+ }
label = _('Netgroups')
label_singular = _('Netgroup')
diff --git a/ipaserver/install/plugins/update_managed_permissions.py b/ipaserver/install/plugins/update_managed_permissions.py
new file mode 100644
index 000000000..603f3f0b7
--- /dev/null
+++ b/ipaserver/install/plugins/update_managed_permissions.py
@@ -0,0 +1,160 @@
+# Authors:
+# Petr Viktorin <pviktori@redhat.com>
+#
+# Copyright (C) 2014 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from ipalib import errors
+from ipapython.dn import DN
+from ipalib.plugable import Registry
+from ipalib.plugins import aci
+from ipalib.plugins.permission import permission
+from ipaserver.plugins.ldap2 import ldap2
+from ipaserver.install.plugins import LAST
+from ipaserver.install.plugins.baseupdate import PostUpdate
+
+
+register = Registry()
+
+
+@register()
+class update_managed_permissions(PostUpdate):
+ """Update managed permissions after an update.
+
+ Update managed permissions according to templates specified in plugins.
+ For read permissions, puts any attributes specified in the legacy
+ Anonymous access ACI in the exclude list when creating the permission.
+ """
+ order = LAST
+
+ def get_anonymous_read_blacklist(self, ldap):
+ """Get the list of attributes from the legacy anonymous access ACI"""
+ aciname = u'Enable Anonymous access'
+ aciprefix = u'none'
+
+ base_entry = ldap.get_entry(self.api.env.basedn, ['aci'])
+
+ acistrs = base_entry.get('aci', [])
+ acilist = aci._convert_strings_to_acis(acistrs)
+ try:
+ rawaci = aci._find_aci_by_name(acilist, aciprefix, aciname)
+ except errors.NotFound:
+ self.log.info('Anonymous ACI not found, using no blacklist')
+ return []
+
+ return rawaci.target['targetattr']['expression']
+
+ def execute(self, **options):
+ ldap = self.api.Backend[ldap2]
+
+ anonymous_read_blacklist = self.get_anonymous_read_blacklist(ldap)
+
+ self.log.info('Anonymous read blacklist: %s', anonymous_read_blacklist)
+
+ for obj in self.api.Object():
+ managed_permissions = getattr(obj, 'managed_permissions', {})
+ if managed_permissions:
+ self.log.info('Updating managed permissions for %s', obj.name)
+ for name, template in managed_permissions.items():
+ assert name.startswith('System:')
+ self.update_permission(ldap,
+ obj,
+ unicode(name),
+ template,
+ anonymous_read_blacklist)
+
+ return False, False, ()
+
+ def update_permission(self, ldap, obj, name, template,
+ anonymous_read_blacklist):
+ """Update the given permission and the corresponding ACI"""
+ dn = self.api.Object[permission].get_dn(name)
+
+ try:
+ attrs_list = self.api.Object[permission].default_attributes
+ entry = ldap.get_entry(dn, attrs_list)
+ is_new = False
+ except errors.NotFound:
+ entry = ldap.make_entry(dn)
+ is_new = True
+
+ self.log.info('Updating managed permission: %s', name)
+ self.update_entry(obj, entry, template,
+ anonymous_read_blacklist, is_new=is_new)
+
+ if is_new:
+ ldap.add_entry(entry)
+ else:
+ try:
+ ldap.update_entry(entry)
+ except errors.EmptyModlist:
+ self.log.debug('No changes to permission: %s', name)
+ return
+
+ self.log.debug('Updating ACI for managed permission: %s', name)
+
+ self.api.Object[permission].update_aci(entry)
+
+ def update_entry(self, obj, entry, template,
+ anonymous_read_blacklist, is_new):
+ """Update the given permission Entry (without contacting LDAP)"""
+
+ [name_ava] = entry.dn[0]
+ assert name_ava.attr == 'cn'
+ name = name_ava.value
+ entry.single_value['cn'] = name
+
+ template = dict(template)
+
+ # Common attributes
+ entry['objectclass'] = self.api.Object[permission].object_class
+
+ entry['ipapermissiontype'] = [u'SYSTEM', u'V2', u'MANAGED']
+
+ # Object-specific attributes
+ ldap_filter = ['(objectclass=%s)' % oc
+ for oc in obj.permission_filter_objectclasses]
+ entry['ipapermtargetfilter'] = ldap_filter
+
+ ipapermlocation = DN(obj.container_dn, self.api.env.basedn)
+ entry.single_value['ipapermlocation'] = ipapermlocation
+
+ # Attributes from template
+ bindruletype = template.pop('ipapermbindruletype')
+ entry.single_value['ipapermbindruletype'] = bindruletype
+
+ entry['ipapermright'] = list(template.pop('ipapermright'))
+
+ # Add to the set of default attributes
+ attributes = set(template.pop('ipapermdefaultattr', ()))
+ attributes.update(entry.get('ipapermdefaultattr', ()))
+ attributes = set(a.lower() for a in attributes)
+ entry['ipapermdefaultattr'] = list(attributes)
+
+ # Exclude attributes filtered from the global read ACI
+ if template.pop('replaces_global_anonymous_aci', False) and is_new:
+ read_blacklist = set(a.lower() for a in anonymous_read_blacklist)
+ read_blacklist &= attributes
+ if read_blacklist:
+ self.log.info('Excluded attributes for %s: %s',
+ name, ', '.join(read_blacklist))
+ entry['ipapermexcludedattr'] = list(read_blacklist)
+
+ # Sanity check
+ if template:
+ raise ValueError(
+ 'Unknown key(s) in managed permission template %s: %s' % (
+ name, ', '.join(template.keys())))