diff options
Diffstat (limited to 'ipalib/plugins/permission.py')
-rw-r--r-- | ipalib/plugins/permission.py | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/ipalib/plugins/permission.py b/ipalib/plugins/permission.py new file mode 100644 index 00000000..c2264aaf --- /dev/null +++ b/ipalib/plugins/permission.py @@ -0,0 +1,363 @@ +# Authors: +# Rob Crittenden <rcritten@redhat.com> +# +# Copyright (C) 2010 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; version 2 only +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" +Permissions + +A permission enables fine-grained delegation of permissions. Access Control +Rules, or instructions (ACIs), grant permission to permissions to perform +given tasks such as adding a user, modifying a group, etc. + +A permission may not be members of other permissions. + +* A permissions grants access to read, write, add or delete. +* A privilege combines similar permissions (for example all the permissions + needed to add a user). +* A role grants a set of privileges to users, groups, hosts or hostgroups. + +A permission is made up of a number of different parts: + +1. The name of the permission. +2. The description of the permission. +3. The target of the permission. +4. The permissions granted by the permission. + +The permissions define what operations are allowed and are one or more of: +1. write - write one or more attributes +2. read - read one or more attributes +3. add - add a new entry to the tree +4. delete - delete an existing entry +5. all - all permissions are granted + +Note the distinction between attributes and entries. The permissions are +independent, so being able to add a user does not mean that the user will +be editabe. + +There are a number of allowed targets: +1. type: a type of object (user, group, etc). +2. memberof: a memberof a group or hostgroup +3. filter: an LDAP filter +4. subtree: an LDAP filter specifying part of the LDAP DIT +5. targetgroup + +EXAMPLES: + + Add a permission that grants the creation of users: + ipa permission-add --desc="Add a User" --type=user --permissions=add adduser + + Add a permission that grants the ability to manage group membership: + ipa permission-add --desc='Manage group members' --attrs=member --permissions=-write --type=group manage_group_members +""" + +import copy +from ipalib.plugins.baseldap import * +from ipalib import api, _, ngettext +from ipalib import Flag, Str, StrEnum +from ipalib.request import context + + +class permission(LDAPObject): + """ + Permission object. + """ + container_dn = api.env.container_permission + object_name = 'permission' + object_name_plural = 'permissions' + object_class = ['groupofnames'] + default_attributes = ['cn', 'description', 'member', 'memberof', + 'memberindirect', + ] + aci_attributes = ['group', 'permissions', 'attrs', 'type', + 'filter', 'subtree', 'targetgroup', + ] + attribute_members = { + 'member': ['privilege'], +# 'memberindirect': ['user', 'group', 'role'], + } + rdnattr='cn' + + label = _('Permissions') + + takes_params = ( + Str('cn', + cli_name='name', + label=_('Permission name'), + primary_key=True, + normalizer=lambda value: value.lower(), + ), + Str('description', + cli_name='desc', + label=_('Description'), + doc=_('Permission description'), + ), + List('permissions', + cli_name='permissions', + label=_('Permissions'), + doc=_('Comma-separated list of permissions to grant ' \ + '(read, write, add, delete, all)'), + ), + List('attrs?', + cli_name='attrs', + label=_('Attributes'), + doc=_('Comma-separated list of attributes'), + ), + StrEnum('type?', + cli_name='type', + label=_('Type'), + doc=_('Type of IPA object (user, group, host, hostgroup, service, netgroup)'), + values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup'), + ), + Str('memberof?', + cli_name='memberof', + label=_('Member of group'), # FIXME: Does this label make sense? + doc=_('Target members of a group'), + ), + Str('filter?', + cli_name='filter', + label=_('Filter'), + doc=_('Legal LDAP filter (e.g. ou=Engineering)'), + ), + Str('subtree?', + cli_name='subtree', + label=_('Subtree'), + doc=_('Subtree to apply permissions to'), + ), + Str('targetgroup?', + cli_name='targetgroup', + label=_('Target group'), + doc=_('User group to apply permissions to'), + ), + ) + +api.register(permission) + + +class permission_add(LDAPCreate): + """ + Add a new permission. + """ + + msg_summary = _('Added permission "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + # Test the ACI before going any further + opts = copy.copy(options) + del opts['description'] + opts['test'] = True + opts['permission'] = keys[-1] + try: + self.api.Command.aci_add(options['description'], **opts) + except Exception, e: + raise e + + # Clear the aci attributes out of the permission entry + for o in options: + try: + if o not in ['description', 'objectclass']: + del entry_attrs[o] + except: + pass + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + # Now actually add the aci. + opts = copy.copy(options) + del opts['description'] + opts['test'] = False + opts['permission'] = keys[-1] + try: + result = self.api.Command.aci_add(options['description'], **opts)['result'] + for attr in self.obj.aci_attributes: + if attr in result: + entry_attrs[attr] = result[attr] + except Exception, e: + self.api.Command.aci_del(keys[-1]) + raise e + return dn + +api.register(permission_add) + + +class permission_del(LDAPDelete): + """ + Delete a permission. + """ + + msg_summary = _('Deleted permission "%(value)s"') + + def pre_callback(self, ldap, dn, *keys, **options): + (dn, entry_attrs) = ldap.get_entry(dn, ['*']) + if 'description' in entry_attrs: + try: + self.api.Command.aci_del(entry_attrs['description'][0]) + except errors.NotFound: + pass + return dn + +api.register(permission_del) + + +class permission_mod(LDAPUpdate): + """ + Modify a permission. + """ + + msg_summary = _('Modified permission "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + (dn, attrs) = ldap.get_entry( + dn, attrs_list, normalize=self.obj.normalize_dn + ) + opts = copy.copy(options) + if 'description' in opts: + del opts['description'] + for o in self.obj.aci_attributes + ['all', 'raw', 'rights']: + if o in opts: + del opts[o] + setattr(context, 'aciupdate', False) + # If there are no options left we don't need to do anything to the + # underlying ACI. + if len(opts) > 0: + opts['test'] = False + opts['permission'] = keys[-1] + try: + self.api.Command.aci_mod(attrs['description'][0], **opts) + setattr(context, 'aciupdate', True) + except Exception, e: + raise e + + # Clear the aci attributes out of the permission entry + for o in self.obj.aci_attributes: + try: + del entry_attrs[o] + except: + pass + + if 'description' in options: + (dn, attrs) = ldap.get_entry(dn, ['description']) + self.api.Command.aci_rename(attrs['description'][0], newname=options['description']) + + return dn + + def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs): + if isinstance(exc, errors.EmptyModlist): + aciupdate = getattr(context, 'aciupdate') + opts = copy.copy(options) + # Clear the aci attributes out of the permission entry + for o in self.obj.aci_attributes + ['all', 'raw', 'rights']: + try: + del opts[o] + except: + pass + + if len(opts) > 0: + raise exc + else: + raise exc + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + result = self.api.Command.permission_show(keys[-1])['result'] + for r in result: + if not r.startswith('member'): + entry_attrs[r] = result[r] + return dn + +api.register(permission_mod) + + +class permission_find(LDAPSearch): + """ + Search for permissions. + """ + + msg_summary = ngettext( + '%(count)d permission matched', '%(count)d permissions matched' + ) + + def post_callback(self, ldap, entries, truncated, *args, **options): + newentries = [] + for entry in entries: + (dn, attrs) = entry + try: + aci = self.api.Command.aci_show(attrs['description'][0])['result'] + for attr in self.obj.aci_attributes: + if attr in aci: + attrs[attr] = aci[attr] + except errors.NotFound: + self.debug('ACI not found for %s' % attrs['description'][0]) + + # Now find all the ACIs that match. Once we find them, add any that + # aren't already in the list along with their permission info. + aciresults = self.api.Command.aci_find(*args, **options) + truncated = truncated or aciresults['truncated'] + results = aciresults['result'] + for aci in results: + found = False + if 'permission' in aci: + for entry in entries: + if aci['permission'] == entry['cn']: + found = True + break + if not found in aci: + permission = self.api.Command.permission_show(aci['permission']) + attrs = permission['result'] + for attr in self.obj.aci_attributes: + if attr in aci: + attrs[attr] = aci[attr] + dn = attrs['dn'] + del attrs['dn'] + newentries.append((dn, attrs)) + + return newentries + +api.register(permission_find) + + +class permission_show(LDAPRetrieve): + """ + Display information about a permission. + """ + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + try: + aci = self.api.Command.aci_show(entry_attrs['description'][0])['result'] + for attr in self.obj.aci_attributes: + if attr in aci: + entry_attrs[attr] = aci[attr] + except errors.NotFound: + self.debug('ACI not found for %s' % entry_attrs['description'][0]) + return dn + +api.register(permission_show) + + +class permission_add_member(LDAPAddMember): + """ + Add members to a permission. + """ + INTERNAL = True + +api.register(permission_add_member) + + +class permission_remove_member(LDAPRemoveMember): + """ + Remove members from a permission. + """ + INTERNAL = True + +api.register(permission_remove_member) |