diff options
Diffstat (limited to 'ipalib')
-rw-r--r-- | ipalib/constants.py | 5 | ||||
-rw-r--r-- | ipalib/errors.py | 8 | ||||
-rw-r--r-- | ipalib/plugins/aci.py | 179 | ||||
-rw-r--r-- | ipalib/plugins/baseldap.py | 264 | ||||
-rw-r--r-- | ipalib/plugins/group.py | 4 | ||||
-rw-r--r-- | ipalib/plugins/host.py | 6 | ||||
-rw-r--r-- | ipalib/plugins/permission.py | 363 | ||||
-rw-r--r-- | ipalib/plugins/privilege.py | 191 | ||||
-rw-r--r-- | ipalib/plugins/role.py | 212 | ||||
-rw-r--r-- | ipalib/plugins/rolegroup.py | 165 | ||||
-rw-r--r-- | ipalib/plugins/service.py | 2 | ||||
-rw-r--r-- | ipalib/plugins/taskgroup.py | 136 | ||||
-rw-r--r-- | ipalib/plugins/user.py | 2 |
13 files changed, 1176 insertions, 361 deletions
diff --git a/ipalib/constants.py b/ipalib/constants.py index 77a30aa57..32c6450dd 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -84,8 +84,9 @@ DEFAULT_CONFIG = ( ('container_service', 'cn=services,cn=accounts'), ('container_host', 'cn=computers,cn=accounts'), ('container_hostgroup', 'cn=hostgroups,cn=accounts'), - ('container_rolegroup', 'cn=rolegroups,cn=accounts'), - ('container_taskgroup', 'cn=taskgroups,cn=accounts'), + ('container_rolegroup', 'cn=roles,cn=accounts'), + ('container_permission', 'cn=permissions,cn=accounts'), + ('container_privilege', 'cn=privileges,cn=accounts'), ('container_automount', 'cn=automount'), ('container_policies', 'cn=policies'), ('container_configs', 'cn=configs,cn=policies'), diff --git a/ipalib/errors.py b/ipalib/errors.py index 86cd60d11..58799628d 100644 --- a/ipalib/errors.py +++ b/ipalib/errors.py @@ -961,12 +961,12 @@ class NotGroupMember(ExecutionError): >>> raise NotGroupMember() Traceback (most recent call last): ... - NotGroupMember: This entry is not a member of the group + NotGroupMember: This entry is not a member """ errno = 4012 - format = _('This entry is not a member of the group') + format = _('This entry is not a member') class RecursiveGroup(ExecutionError): """ @@ -993,12 +993,12 @@ class AlreadyGroupMember(ExecutionError): >>> raise AlreadyGroupMember() Traceback (most recent call last): ... - AlreadyGroupMember: This entry is already a member of the group + AlreadyGroupMember: This entry is already a member """ errno = 4014 - format = _('This entry is already a member of the group') + format = _('This entry is already a member') class Base64DecodeError(ExecutionError): """ diff --git a/ipalib/plugins/aci.py b/ipalib/plugins/aci.py index 153798924..c0f47e301 100644 --- a/ipalib/plugins/aci.py +++ b/ipalib/plugins/aci.py @@ -28,11 +28,11 @@ existing entries or adding or deleting new ones. The goal of the ACIs that ship with IPA is to provide a set of low-level permissions that grant access to special groups called taskgroups. These low-level permissions can be combined into roles that grant broader access. These -roles are another type of group, rolegroups. +roles are another type of group, roles. For example, if you have taskgroups that allow adding and modifying users you -could create a rolegroup, useradmin. You would assign users to the useradmin -rolegroup to allow them to do the operations defined by the taskgroups. +could create a role, useradmin. You would assign users to the useradmin +role to allow them to do the operations defined by the taskgroups. You can create ACIs that delegate permission so users in group A can write attributes on group B. @@ -64,7 +64,7 @@ be editabe. The bind rule defines who this ACI grants permissions to. The LDAP server allows this to be any valid LDAP entry but we encourage the use of -taskgroups so that the rights can be easily shared through rolegroups. +taskgroups so that the rights can be easily shared through roles. For a more thorough description of access controls see http://www.redhat.com/docs/manuals/dir-server/ag/8.0/Managing_Access_Control.html @@ -80,6 +80,9 @@ EXAMPLES: Add an ACI that allows members of the "addusers" taskgroup to add new users: ipa aci-add --type=user --taskgroup=addusers --permissions=add "Add new users" + Add an ACI that lets members of the edotors manage members of the admins group: + ipa aci-add --permissions=write --attrs=member --targetgroup=admins --group=editors "Editors manage admins" + The show command shows the raw 389-ds ACI. IMPORTANT: When modifying the target attributes of an existing ACI you @@ -148,25 +151,25 @@ def _make_aci(current, aciname, kw): raise errors.ValidationError(name='target', error=_('at least one of: type, filter, subtree, targetgroup, attrs or memberof are required')) group = 'group' in kw - taskgroup = 'taskgroup' in kw + permission = 'permission' in kw selfaci = 'selfaci' in kw and kw['selfaci'] == True - if group + taskgroup + selfaci > 1: - raise errors.ValidationError(name='target', error=_('group, taskgroup and self are mutually exclusive')) - elif group + taskgroup + selfaci == 0: - raise errors.ValidationError(name='target', error=_('One of group, taskgroup or self is required')) + if group + permission + selfaci > 1: + raise errors.ValidationError(name='target', error=_('group, permission and self are mutually exclusive')) + elif group + permission + selfaci == 0: + raise errors.ValidationError(name='target', error=_('One of group, permission or self is required')) # Grab the dn of the group we're granting access to. This group may be a - # taskgroup or a user group. + # permission or a user group. entry_attrs = [] - if taskgroup: + if permission: + # This will raise NotFound if the permission doesn't exist try: - entry_attrs = api.Command['taskgroup_show'](kw['taskgroup'])['result'] - except errors.NotFound: - # The task group doesn't exist, let's be helpful and add it - tgkw = {'description': aciname} - entry_attrs = api.Command['taskgroup_add']( - kw['taskgroup'], **tgkw - )['result'] + entry_attrs = api.Command['permission_show'](kw['permission'])['result'] + except errors.NotFound, e: + if 'test' in kw and not kw.get('test'): + raise e + else: + entry_attrs = {'dn': 'cn=%s,%s' % (kw['permission'], api.env.container_permission)} elif group: # Not so friendly with groups. This will raise try: @@ -186,7 +189,7 @@ def _make_aci(current, aciname, kw): a.set_target_attr(kw['attrs']) if 'memberof' in kw: entry_attrs = api.Command['group_show'](kw['memberof'])['result'] - a.set_target_filter('memberOf=%s' % dn) + a.set_target_filter('memberOf=%s' % entry_attrs['dn']) if 'filter' in kw: a.set_target_filter(kw['filter']) if 'type' in kw: @@ -195,7 +198,7 @@ def _make_aci(current, aciname, kw): if 'targetgroup' in kw: # Purposely no try here so we'll raise a NotFound entry_attrs = api.Command['group_show'](kw['targetgroup'])['result'] - target = 'ldap:///%s' % dn + target = 'ldap:///%s' % entry_attrs['dn'] a.set_target(target) if 'subtree' in kw: # See if the subtree is a full URI @@ -206,7 +209,7 @@ def _make_aci(current, aciname, kw): return a -def _aci_to_kw(ldap, a): +def _aci_to_kw(ldap, a, test=False): """Convert an ACI into its equivalent keywords. This is used for the modify operation so we can merge the @@ -254,11 +257,20 @@ def _aci_to_kw(ldap, a): pass else: if groupdn.startswith('cn='): - (dn, entry_attrs) = ldap.get_entry(groupdn, ['cn']) - if api.env.container_taskgroup in dn: - kw['taskgroup'] = entry_attrs['cn'][0] + dn = '' + entry_attrs = {} + try: + (dn, entry_attrs) = ldap.get_entry(groupdn, ['cn']) + except errors.NotFound, e: + # FIXME, use real name here + if test: + dn = 'cn=%s,%s' % ('test', api.env.container_permission) + entry_attrs = {'cn': [u'test']} + if api.env.container_permission in dn: + kw['permission'] = entry_attrs['cn'][0] else: - kw['group'] = entry_attrs['cn'][0] + if 'cn' in entry_attrs: + kw['group'] = entry_attrs['cn'][0] return kw @@ -299,6 +311,7 @@ class aci(Object): """ ACI object. """ + INTERNAL = True label = _('ACIs') @@ -308,10 +321,10 @@ class aci(Object): label=_('ACI name'), primary_key=True, ), - Str('taskgroup?', - cli_name='taskgroup', - label=_('Taskgroup'), - doc=_('Taskgroup ACI grants access to'), + Str('permission?', + cli_name='permission', + label=_('Permission'), + doc=_('Permission ACI grants access to'), ), Str('group?', cli_name='group', @@ -370,8 +383,16 @@ class aci_add(crud.Create): """ Create new ACI. """ + INTERNAL = True msg_summary = _('Created ACI "%(value)s"') + takes_options = ( + Flag('test?', + doc=_('Test the ACI syntax but don\'t write anything'), + default=False, + ), + ) + def execute(self, aciname, **kw): """ Execute the aci-create operation. @@ -390,18 +411,20 @@ class aci_add(crud.Create): acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) for a in acis: - if a.isequal(newaci): + # FIXME: add check for permission_group = permission_group + if a.isequal(newaci) or newaci.name == a.name: raise errors.DuplicateEntry() newaci_str = unicode(newaci) entry_attrs['aci'].append(newaci_str) - ldap.update_entry(dn, entry_attrs) + if not kw.get('test', False): + ldap.update_entry(dn, entry_attrs) if kw.get('raw', False): result = dict(aci=unicode(newaci_str)) else: - result = _aci_to_kw(ldap, newaci) + result = _aci_to_kw(ldap, newaci, kw.get('test', False)) return dict( result=result, value=aciname, @@ -414,6 +437,7 @@ class aci_del(crud.Delete): """ Delete ACI. """ + INTERNAL = True has_output = output.standard_delete msg_summary = _('Deleted ACI "%(value)s"') @@ -454,6 +478,7 @@ class aci_mod(crud.Update): """ Modify ACI. """ + INTERNAL = True has_output_params = ( Str('aci', label=_('ACI'), @@ -485,6 +510,8 @@ class aci_mod(crud.Update): # _make_aci is what is run in aci_add and validates the input. # Do this before we delete the existing ACI. newaci = _make_aci(None, aciname, newkw) + if aci.isequal(newaci): + raise errors.EmptyModlist() self.api.Command['aci_del'](aciname) @@ -522,6 +549,7 @@ class aci_find(crud.Search): have ipausers as a memberof. There may be other ACIs that apply to members of that group indirectly. """ + INTERNAL = True msg_summary = ngettext('%(count)d ACI matched', '%(count)d ACIs matched', 0) def execute(self, term, **kw): @@ -560,10 +588,10 @@ class aci_find(crud.Search): results.remove(a) acis = list(results) - if 'taskgroup' in kw: + if 'permission' in kw: try: - self.api.Command['taskgroup_show']( - kw['taskgroup'] + self.api.Command['permission_show']( + kw['permission'] ) except errors.NotFound: pass @@ -600,7 +628,24 @@ class aci_find(crud.Search): # uncomment next line if you add more search criteria # acis = list(results) - # TODO: searching by: type, filter, subtree + for a in acis: + if 'target' in a.target: + target = a.target['target']['expression'] + else: + results.remove(a) + continue + found = False + for k in _type_map.keys(): + if _type_map[k] == target and 'type' in kw and kw['type'] == k: + found = True + break; + if not found: + try: + results.remove(a) + except ValueError: + pass + + # TODO: searching by: filter, subtree acis = [] for result in results: @@ -623,6 +668,7 @@ class aci_show(crud.Retrieve): """ Display a single ACI given an ACI name. """ + INTERNAL = True has_output_params = ( Str('aci', @@ -656,3 +702,64 @@ class aci_show(crud.Retrieve): ) api.register(aci_show) + + +class aci_rename(crud.Update): + """ + Rename an ACI. + """ + INTERNAL = True + has_output_params = ( + Str('aci', + label=_('ACI'), + ), + ) + + takes_options = ( + Str('newname', + doc=_('New ACI name'), + ), + ) + + msg_summary = _('Renameed ACI to "%(value)s"') + + def execute(self, aciname, **kw): + ldap = self.api.Backend.ldap2 + + (dn, entry_attrs) = ldap.get_entry(self.api.env.basedn, ['aci']) + + acis = _convert_strings_to_acis(entry_attrs.get('aci', [])) + aci = _find_aci_by_name(acis, aciname) + + for a in acis: + if kw['newname'] == a.name: + raise errors.DuplicateEntry() + + # The strategy here is to convert the ACI we're updating back into + # a series of keywords. Then we replace any keywords that have been + # updated and convert that back into an ACI and write it out. + newkw = _aci_to_kw(ldap, aci) + if 'selfaci' in newkw and newkw['selfaci'] == True: + # selfaci is set in aci_to_kw to True only if the target is self + kw['selfaci'] = True + if 'aciname' in newkw: + del newkw['aciname'] + + # _make_aci is what is run in aci_add and validates the input. + # Do this before we delete the existing ACI. + newaci = _make_aci(None, kw['newname'], newkw) + + self.api.Command['aci_del'](aciname) + + result = self.api.Command['aci_add'](kw['newname'], **newkw)['result'] + + if kw.get('raw', False): + result = dict(aci=unicode(newaci)) + else: + result = _aci_to_kw(ldap, newaci) + return dict( + result=result, + value=kw['newname'], + ) + +api.register(aci_rename) diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index a67b84d09..3894e18de 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -42,17 +42,29 @@ global_output_params = ( Str('member_group?', label=_('Member groups'), ), + Str('memberof_group?', + label=_('Member of groups'), + ), Str('member_host?', label=_('Member hosts'), ), Str('memberof_hostgroup?', label=_('Member of host-groups'), ), - Str('memberof_taskgroup?', - label=_('Member of task-groups'), + Str('memberof_permission?', + label=_('Permissions'), + ), + Str('memberof_privilege?', + label='Privileges', + ), + Str('memberof_role?', + label=_('Roles'), ), - Str('member_rolegroup?', - label=_('Member role-groups'), + Str('member_privilege?', + label='Granted to Privilege', + ), + Str('member_role?', + label=_('Granting privilege to roles'), ), Str('member_netgroup?', label=_('Member netgroups'), @@ -93,11 +105,11 @@ global_output_params = ( Str('memberindirect_hostgroup?', label=_('Indirect Member host-groups'), ), - Str('memberindirect_rolegroup?', - label=_('Indirect Member role-groups'), + Str('memberindirect_role?', + label=_('Indirect Member of roles'), ), - Str('memberindirect_taskgroup?', - label=_('Indirect Member role-groups'), + Str('memberindirect_permission?', + label=_('Indirect Member permissions'), ), Str('memberindirect_hbacsvc?', label=_('Indirect Member HBAC service'), @@ -1192,9 +1204,11 @@ class LDAPSearch(CallbackInterface, crud.Search): for callback in self.POST_CALLBACKS: if hasattr(callback, 'im_self'): - callback(ldap, entries, truncated, *args, **options) + more = callback(ldap, entries, truncated, *args, **options) else: - callback(self, ldap, entries, truncated, *args, **options) + more = callback(self, ldap, entries, truncated, *args, **options) + if more: + entries = entries + more if not options.get('raw', False): for e in entries: @@ -1214,8 +1228,236 @@ class LDAPSearch(CallbackInterface, crud.Search): return (filter, base_dn, scope) def post_callback(self, ldap, entries, truncated, *args, **options): - pass + return [] def exc_callback(self, args, options, exc, call_func, *call_args, **call_kwargs): raise exc + +class LDAPModReverseMember(LDAPQuery): + """ + Base class for reverse member manipulation. + """ + reverse_attributes = ['member'] + reverse_param_doc = 'comma-separated list of %s' + reverse_count_out = ('%i member processed.', '%i members processed.') + + has_output_params = global_output_params + + def get_options(self): + for option in super(LDAPModReverseMember, self).get_options(): + yield option + for attr in self.reverse_attributes: + for ldap_obj_name in self.obj.reverse_members[attr]: + ldap_obj = self.api.Object[ldap_obj_name] + name = to_cli(ldap_obj_name) + doc = self.reverse_param_doc % ldap_obj.object_name_plural + yield List('%s?' % name, cli_name='%ss' % name, doc=doc, + label=ldap_obj.object_name) + + +class LDAPAddReverseMember(LDAPModReverseMember): + """ + Add other LDAP entries to members in reverse. + + The call looks like "add A to B" but in fact executes + add B to A to handle reverse membership. + """ + member_param_doc = 'comma-separated list of %s to add' + member_count_out = ('%i member added.', '%i members added.') + + show_command = None + member_command = None + reverse_attr = None + member_attr = None + + has_output = ( + output.Entry('result'), + output.Output('failed', + type=dict, + doc=_('Members that could not be added'), + ), + output.Output('completed', + type=int, + doc=_('Number of members added'), + ), + ) + + has_output_params = global_output_params + + def execute(self, *keys, **options): + ldap = self.obj.backend + + # Ensure our target exists + result = self.api.Command[self.show_command](keys[-1])['result'] + dn = result['dn'] + + for callback in self.PRE_CALLBACKS: + if hasattr(callback, 'im_self'): + dn = callback(ldap, dn, *keys, **options) + else: + dn = callback( + self, ldap, dn, *keys, **options + ) + + if options.get('all', False): + attrs_list = ['*'] + self.obj.default_attributes + else: + attrs_list = self.obj.default_attributes + + completed = 0 + failed = {'member': {self.reverse_attr: []}} + for attr in options.get(self.reverse_attr, []): + try: + options = {'%s' % self.member_attr: keys[-1]} + try: + result = self.api.Command[self.member_command](attr, **options) + if result['completed'] == 1: + completed = completed + 1 + else: + failed['member'][self.reverse_attr].append((attr, result['failed']['member'][self.member_attr][0][1])) + except errors.ExecutionError, e: + try: + (dn, entry_attrs) = self._call_exc_callbacks( + keys, options, e, self.member_command, dn, attrs_list, + normalize=self.obj.normalize_dn + ) + except errors.NotFound, e: + msg = str(e) + (attr, msg) = msg.split(':', 1) + failed['member'][self.reverse_attr].append((attr, unicode(msg.strip()))) + + except errors.PublicError, e: + failed['member'][self.reverse_attr].append((attr, unicode(msg))) + + entry_attrs = self.api.Command[self.show_command](keys[-1])['result'] + + for callback in self.POST_CALLBACKS: + if hasattr(callback, 'im_self'): + (completed, dn) = callback( + ldap, completed, failed, dn, entry_attrs, *keys, **options + ) + else: + (completed, dn) = callback( + self, ldap, completed, failed, dn, entry_attrs, *keys, + **options + ) + + entry_attrs['dn'] = dn + return dict( + completed=completed, + failed=failed, + result=entry_attrs, + ) + + def pre_callback(self, ldap, dn, *keys, **options): + return dn + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + return (completed, dn) + + def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs): + raise exc + +class LDAPRemoveReverseMember(LDAPModReverseMember): + """ + Remove other LDAP entries from members in reverse. + + The call looks like "remove A from B" but in fact executes + remove B from A to handle reverse membership. + """ + member_param_doc = 'comma-separated list of %s to remove' + member_count_out = ('%i member removed.', '%i members removed.') + + show_command = None + member_command = None + reverse_attr = None + member_attr = None + + has_output = ( + output.Entry('result'), + output.Output('failed', + type=dict, + doc=_('Members that could not be removed'), + ), + output.Output('completed', + type=int, + doc=_('Number of members removed'), + ), + ) + + has_output_params = global_output_params + + def execute(self, *keys, **options): + ldap = self.obj.backend + + # Ensure our target exists + result = self.api.Command[self.show_command](keys[-1])['result'] + dn = result['dn'] + + for callback in self.PRE_CALLBACKS: + if hasattr(callback, 'im_self'): + dn = callback(ldap, dn, *keys, **options) + else: + dn = callback( + self, ldap, dn, *keys, **options + ) + + if options.get('all', False): + attrs_list = ['*'] + self.obj.default_attributes + else: + attrs_list = self.obj.default_attributes + + completed = 0 + failed = {'member': {self.reverse_attr: []}} + for attr in options.get(self.reverse_attr, []): + try: + options = {'%s' % self.member_attr: keys[-1]} + try: + result = self.api.Command[self.member_command](attr, **options) + if result['completed'] == 1: + completed = completed + 1 + else: + failed['member'][self.reverse_attr].append((attr, result['failed']['member'][self.member_attr][0][1])) + except errors.ExecutionError, e: + try: + (dn, entry_attrs) = self._call_exc_callbacks( + keys, options, e, self.member_command, dn, attrs_list, + normalize=self.obj.normalize_dn + ) + except errors.NotFound, e: + msg = str(e) + (attr, msg) = msg.split(':', 1) + failed['member'][self.reverse_attr].append((attr, unicode(msg.strip()))) + + except errors.PublicError, e: + failed['member'][self.reverse_attr].append((attr, unicode(msg))) + + entry_attrs = self.api.Command[self.show_command](keys[-1])['result'] + + for callback in self.POST_CALLBACKS: + if hasattr(callback, 'im_self'): + (completed, dn) = callback( + ldap, completed, failed, dn, entry_attrs, *keys, **options + ) + else: + (completed, dn) = callback( + self, ldap, completed, failed, dn, entry_attrs, *keys, + **options + ) + + entry_attrs['dn'] = dn + return dict( + completed=completed, + failed=failed, + result=entry_attrs, + ) + + def pre_callback(self, ldap, dn, *keys, **options): + return dn + + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): + return (completed, dn) + + def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs): + raise exc diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py index f26792a56..290d7ba1d 100644 --- a/ipalib/plugins/group.py +++ b/ipalib/plugins/group.py @@ -89,8 +89,8 @@ class group(LDAPObject): uuid_attribute = 'ipauniqueid' attribute_members = { 'member': ['user', 'group'], - 'memberof': ['group', 'netgroup', 'rolegroup', 'taskgroup'], - 'memberindirect': ['user', 'group', 'netgroup', 'rolegroup', 'taskgroup'], + 'memberof': ['group', 'netgroup', 'role',], + 'memberindirect': ['user', 'group', 'netgroup', 'role'], } rdnattr = 'cn' diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py index b3caf1851..a9589c6ec 100644 --- a/ipalib/plugins/host.py +++ b/ipalib/plugins/host.py @@ -31,10 +31,10 @@ ENROLLMENT: There are three enrollment scenarios when enrolling a new client: 1. You are enrolling as a full administrator. The host entry may exist - or not. A full administrator is a member of the hostadmin rolegroup + or not. A full administrator is a member of the hostadmin role or the admins group. 2. You are enrolling as a limited administrator. The host must already - exist. A limited administrator is a member of the enrollhost rolegroup. + exist. A limited administrator is a member of the enrollhost role. 3. The host has been created with a one-time password. A host can only be enrolled once. If a client has enrolled and needs to @@ -162,7 +162,7 @@ class host(LDAPObject): uuid_attribute = 'ipauniqueid' attribute_members = { 'enrolledby': ['user'], - 'memberof': ['hostgroup', 'netgroup', 'rolegroup'], + 'memberof': ['hostgroup', 'netgroup', 'role'], 'managedby': ['host'], } diff --git a/ipalib/plugins/permission.py b/ipalib/plugins/permission.py new file mode 100644 index 000000000..c2264aaf3 --- /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) diff --git a/ipalib/plugins/privilege.py b/ipalib/plugins/privilege.py new file mode 100644 index 000000000..f412448fc --- /dev/null +++ b/ipalib/plugins/privilege.py @@ -0,0 +1,191 @@ +# 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 +""" +Privileges + +A privilege enables fine-grained delegation of permissions. Access Control +Rules, or instructions (ACIs), grant permission to privileges to perform +given tasks such as adding a user, modifying a group, etc. + +A privilege may not be members of other privileges. + +See role and permission for additional information. +""" + +from ipalib.plugins.baseldap import * +from ipalib import api, _, ngettext + + +class privilege(LDAPObject): + """ + Privilege object. + """ + container_dn = api.env.container_privilege + object_name = 'privilege' + object_name_plural = 'privileges' + object_class = ['nestedgroup', 'groupofnames'] + default_attributes = ['cn', 'description', 'member', 'memberof', + 'memberindirect' + ] + attribute_members = { + 'member': ['permission', 'role'], + 'memberof': ['permission'], +# 'memberindirect': ['permission'], + # FIXME: privilege can be member of ??? + } + reverse_members = { + 'member': ['permission'], + } + rdnattr='cn' + + label = _('Privileges') + + takes_params = ( + Str('cn', + cli_name='name', + label=_('Privilege name'), + primary_key=True, + normalizer=lambda value: value.lower(), + ), + Str('description', + cli_name='desc', + label=_('Description'), + doc=_('Privilege description'), + ), + ) + +api.register(privilege) + + +class privilege_add(LDAPCreate): + """ + Add a new privilege. + """ + + msg_summary = _('Added privilege "%(value)s"') + +api.register(privilege_add) + + +class privilege_del(LDAPDelete): + """ + Delete a privilege. + """ + + msg_summary = _('Deleted privilege "%(value)s"') + +api.register(privilege_del) + + +class privilege_mod(LDAPUpdate): + """ + Modify a privilege. + """ + + msg_summary = _('Modified privilege "%(value)s"') + +api.register(privilege_mod) + + +class privilege_find(LDAPSearch): + """ + Search for privileges. + """ + + msg_summary = ngettext( + '%(count)d privilege matched', '%(count)d privileges matched' + ) + +api.register(privilege_find) + + +class privilege_show(LDAPRetrieve): + """ + Display information about a privilege. + """ + +api.register(privilege_show) + + +class privilege_add_member(LDAPAddMember): + """ + Add members to a privilege + """ + INTERNAL=True + +api.register(privilege_add_member) + + +class privilege_remove_member(LDAPRemoveMember): + """ + Remove members from a privilege + """ + INTERNAL=True + +api.register(privilege_remove_member) + + +class privilege_add_permission(LDAPAddReverseMember): + """ + Add permissions to a privilege. + """ + show_command = 'privilege_show' + member_command = 'permission_add_member' + reverse_attr = 'permission' + member_attr = 'privilege' + + has_output = ( + output.Entry('result'), + output.Output('failed', + type=dict, + doc=_('Members that could not be added'), + ), + output.Output('completed', + type=int, + doc=_('Number of permissions added'), + ), + ) + +api.register(privilege_add_permission) + + +class privilege_remove_permission(LDAPRemoveReverseMember): + """ + Remove permissions from a privilege. + """ + show_command = 'privilege_show' + member_command = 'permission_remove_member' + reverse_attr = 'permission' + member_attr = 'privilege' + + permission_count_out = ('%i permission removed.', '%i permissions removed.') + + has_output = ( + output.Entry('result'), + output.Output('failed', + type=dict, + doc=_('Members that could not be added'), + ), + output.Output('completed', + type=int, + doc=_('Number of permissions removed'), + ), + ) + +api.register(privilege_remove_permission) diff --git a/ipalib/plugins/role.py b/ipalib/plugins/role.py new file mode 100644 index 000000000..ceca75ffb --- /dev/null +++ b/ipalib/plugins/role.py @@ -0,0 +1,212 @@ +# Authors: +# Rob Crittenden <rcritten@redhat.com> +# Pavel Zuna <pzuna@redhat.com> +# +# Copyright (C) 2009 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 +""" +Roles + +A role is used for fine-grained delegation. A permission grants the ability +to perform given low-level tasks (add a user, modify a group, etc.). A +privilege combines one or more permissions into a higher-level abstraction +such as useradmin. A useradmin would be able to add, delete and modify users. + +Privileges are assigned to Roles. + +Users, groups, hosts and hostgroups may be members of a Role. + +Roles can not contain other roles. + +EXAMPLES: + + Add a new role: + ipa role-add --desc="Junior-level admin" junioradmin + + Add some privileges to this role: + ipa role-add-privilege --privileges=addusers junioradmin + ipa role-add-privilege --privileges=change_password junioradmin + ipa role-add-privilege --privileges=add_user_to_default_group juioradmin + + Add a group of users to this role: + ipa group-add --desc="User admins" useradmins + ipa role-add-member --groups=useradmins junioradmin + + Display information about a role: + ipa role-show junioradmin + + The result of this is that any users in the group 'useradmins' can + add users, reset passwords or add a user to the default IPA user group. +""" + +from ipalib.plugins.baseldap import * +from ipalib import api, Str, _, ngettext +from ipalib import Command +from ipalib.plugins import privilege + + +class role(LDAPObject): + """ + Role object. + """ + container_dn = api.env.container_rolegroup + object_name = 'role' + object_name_plural = 'roles' + object_class = ['groupofnames', 'nestedgroup'] + default_attributes = ['cn', 'description', 'member', 'memberof', + 'memberindirect' + ] + attribute_members = { + 'member': ['user', 'group', 'host', 'hostgroup'], + 'memberof': ['privilege'], +# 'memberindirect': ['user', 'group', 'host', 'hostgroup'], + } + reverse_members = { + 'member': ['privilege'], + } + rdnattr='cn' + + label = _('Role Groups') + + takes_params = ( + Str('cn', + cli_name='name', + label=_('Role name'), + primary_key=True, + normalizer=lambda value: value.lower(), + ), + Str('description', + cli_name='desc', + label=_('Description'), + doc=_('A description of this role-group'), + ), + ) + +api.register(role) + + +class role_add(LDAPCreate): + """ + Add a new role. + """ + + msg_summary = _('Added role "%(value)s"') + +api.register(role_add) + + +class role_del(LDAPDelete): + """ + Delete a role. + """ + + msg_summary = _('Deleted role "%(value)s"') + +api.register(role_del) + + +class role_mod(LDAPUpdate): + """ + Modify a role. + """ + + msg_summary = _('Modified role "%(value)s"') + +api.register(role_mod) + + +class role_find(LDAPSearch): + """ + Search for roles. + """ + + msg_summary = ngettext( + '%(count)d role matched', '%(count)d roles matched' + ) + +api.register(role_find) + + +class role_show(LDAPRetrieve): + """ + Display information about a role. + """ + +api.register(role_show) + + +class role_add_member(LDAPAddMember): + """ + Add members to a role. + """ + +api.register(role_add_member) + + +class role_remove_member(LDAPRemoveMember): + """ + Remove members from a role. + """ + +api.register(role_remove_member) + + +class role_add_privilege(LDAPAddReverseMember): + """ + Add privileges to a role. + """ + show_command = 'role_show' + member_command = 'privilege_add_member' + reverse_attr = 'privilege' + member_attr = 'role' + + has_output = ( + output.Entry('result'), + output.Output('failed', + type=dict, + doc=_('Members that could not be added'), + ), + output.Output('completed', + type=int, + doc=_('Number of privileges added'), + ), + ) + +api.register(role_add_privilege) + + +class role_remove_privilege(LDAPRemoveReverseMember): + """ + Remove privileges from a role. + """ + show_command = 'role_show' + member_command = 'privilege_remove_member' + reverse_attr = 'privilege' + member_attr = 'role' + + has_output = ( + output.Entry('result'), + output.Output('failed', + type=dict, + doc=_('Members that could not be added'), + ), + output.Output('completed', + type=int, + doc=_('Number of privileges removed'), + ), + ) + +api.register(role_remove_privilege) diff --git a/ipalib/plugins/rolegroup.py b/ipalib/plugins/rolegroup.py deleted file mode 100644 index e0b6fbc4e..000000000 --- a/ipalib/plugins/rolegroup.py +++ /dev/null @@ -1,165 +0,0 @@ -# Authors: -# Rob Crittenden <rcritten@redhat.com> -# Pavel Zuna <pzuna@redhat.com> -# -# Copyright (C) 2009 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 -""" -Rolegroups - -A rolegroup is used for fine-grained delegation. Access control rules -(ACIs) grant permission to perform given tasks (add a user, modify a group, -etc.), to task groups. Rolegroups are members of taskgroups, giving them -permission to perform the task. - -The logic behind ACIs and rolegroups proceeds as follows: - - ACIs grants permission to taskgroup - rolegroups are members of taskgroups - users, groups, hosts and hostgroups are members of rolegroups - -Rolegroups can contain both hosts and hostgroups, enabling -operations using the host service principal associated with a machine. - -Rolegroups can not contain other rolegroups. - -EXAMPLES: - - Add a new rolegroup: - ipa rolegroup-add --desc="Junior-level admin" junioradmin - - Add this role to some tasks: - ipa taskgroup-add-member --rolegroups=junioradmin addusers - ipa taskgroup-add-member --rolegroups=junioradmin change_password - ipa taskgroup-add-member --rolegroups=junioradmin add_user_to_default_group - - Yes, this can seem backwards. The taskgroup is the entry that is granted - permissions by the ACIs. By adding a rolegroup as a member of a taskgroup - it inherits those permissions. - - Add a group of users to this role: - ipa group-add --desc="User admins" useradmins - ipa rolegroup-add-member --groups=useradmins junioradmin - - Display information about a rolegroup: - ipa rolegroup-show junioradmin -""" - -from ipalib.plugins.baseldap import * -from ipalib import api, Str, _, ngettext - - -class rolegroup(LDAPObject): - """ - Rolegroup object. - """ - container_dn = api.env.container_rolegroup - object_name = 'rolegroup' - object_name_plural = 'rolegroups' - object_class = ['groupofnames', 'nestedgroup'] - default_attributes = ['cn', 'description', 'member', 'memberof', - 'memberindirect' - ] - attribute_members = { - 'member': ['user', 'group', 'host', 'hostgroup'], - 'memberof': ['taskgroup'], - 'memberindirect': ['user', 'group', 'host', 'hostgroup'], - } - rdnattr='cn' - - label = _('Role Groups') - - takes_params = ( - Str('cn', - cli_name='name', - label=_('Role-group name'), - primary_key=True, - normalizer=lambda value: value.lower(), - ), - Str('description', - cli_name='desc', - label=_('Description'), - doc=_('A description of this role-group'), - ), - ) - -api.register(rolegroup) - - -class rolegroup_add(LDAPCreate): - """ - Add a new rolegroup. - """ - - msg_summary = _('Added rolegroup "%(value)s"') - -api.register(rolegroup_add) - - -class rolegroup_del(LDAPDelete): - """ - Delete a rolegroup. - """ - - msg_summary = _('Deleted rolegroup "%(value)s"') - -api.register(rolegroup_del) - - -class rolegroup_mod(LDAPUpdate): - """ - Modify a rolegroup. - """ - - msg_summary = _('Modified rolegroup "%(value)s"') - -api.register(rolegroup_mod) - - -class rolegroup_find(LDAPSearch): - """ - Search for rolegroups. - """ - - msg_summary = ngettext( - '%(count)d rolegroup matched', '%(count)d rolegroups matched' - ) - -api.register(rolegroup_find) - - -class rolegroup_show(LDAPRetrieve): - """ - Display information about a rolegroup. - """ - -api.register(rolegroup_show) - - -class rolegroup_add_member(LDAPAddMember): - """ - Add members to a rolegroup. - """ - -api.register(rolegroup_add_member) - - -class rolegroup_remove_member(LDAPRemoveMember): - """ - Remove members from a rolegroup. - """ - -api.register(rolegroup_remove_member) diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py index 6cdba9b32..fbb1ff2ca 100644 --- a/ipalib/plugins/service.py +++ b/ipalib/plugins/service.py @@ -47,7 +47,7 @@ EXAMPLES: Allow a host to manage an IPA service certificate: ipa service-add-host --hosts=web.example.com HTTP/web.example.com - ipa rolegroup-add-member --hosts=web.example.com certadmin + ipa role-add-member --hosts=web.example.com certadmin Delete an IPA service: ipa service-del HTTP/web.example.com diff --git a/ipalib/plugins/taskgroup.py b/ipalib/plugins/taskgroup.py deleted file mode 100644 index ba3f50738..000000000 --- a/ipalib/plugins/taskgroup.py +++ /dev/null @@ -1,136 +0,0 @@ -# Authors: -# Rob Crittenden <rcritten@redhat.com> -# Pavel Zuna <pzuna@redhat.com> -# -# Copyright (C) 2009 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 -""" -Taskgroups - -A taskgroup enables fine-grained delegation of permissions. Access Control -Rules, or instructions (ACIs), grant permission to taskgroups to perform -given tasks such as adding a user, modifying a group, etc. - -A taskgroup may not be members of other taskgroups. - -See rolegroup and aci for additional information. -""" - -from ipalib.plugins.baseldap import * -from ipalib import api, _, ngettext - - -class taskgroup(LDAPObject): - """ - Taskgroup object. - """ - container_dn = api.env.container_taskgroup - object_name = 'taskgroup' - object_name_plural = 'taskgroups' - object_class = ['groupofnames'] - default_attributes = ['cn', 'description', 'member', 'memberof', - 'memberindirect' - ] - attribute_members = { - 'member': ['user', 'group', 'rolegroup'], - 'memberindirect': ['user', 'group', 'rolegroup'], - # FIXME: taskgroup can be member of ??? - } - rdnattr='cn' - - label = _('Task Groups') - - takes_params = ( - Str('cn', - cli_name='name', - label=_('Task-group name'), - primary_key=True, - normalizer=lambda value: value.lower(), - ), - Str('description', - cli_name='desc', - label=_('Description'), - doc=_('Task-group description'), - ), - ) - -api.register(taskgroup) - - -class taskgroup_add(LDAPCreate): - """ - Add a new taskgroup. - """ - - msg_summary = _('Added taskgroup "%(value)s"') - -api.register(taskgroup_add) - - -class taskgroup_del(LDAPDelete): - """ - Delete a taskgroup. - """ - - msg_summary = _('Deleted taskgroup "%(value)s"') - -api.register(taskgroup_del) - - -class taskgroup_mod(LDAPUpdate): - """ - Modify a taskgroup. - """ - - msg_summary = _('Modified taskgroup "%(value)s"') - -api.register(taskgroup_mod) - - -class taskgroup_find(LDAPSearch): - """ - Search for taskgroups. - """ - - msg_summary = ngettext( - '%(count)d taskgroup matched', '%(count)d taskgroups matched' - ) - -api.register(taskgroup_find) - - -class taskgroup_show(LDAPRetrieve): - """ - Display information about a taskgroup. - """ - -api.register(taskgroup_show) - - -class taskgroup_add_member(LDAPAddMember): - """ - Add members to a taskgroup. - """ - -api.register(taskgroup_add_member) - - -class taskgroup_remove_member(LDAPRemoveMember): - """ - Remove members from a taskgroup. - """ - -api.register(taskgroup_remove_member) diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index 64120fd2b..1bbb9b1ae 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -70,7 +70,7 @@ class user(LDAPObject): ] uuid_attribute = 'ipauniqueid' attribute_members = { - 'memberof': ['group', 'netgroup', 'rolegroup', 'taskgroup'], + 'memberof': ['group', 'netgroup', 'role'], } rdnattr = 'uid' |