summaryrefslogtreecommitdiffstats
path: root/ipalib/plugins
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2010-12-01 11:23:52 -0500
committerSimo Sorce <ssorce@redhat.com>2010-12-01 20:42:31 -0500
commit4ad8055341b9f12c833abdf757755ed95f1b375e (patch)
tree1733fffdfe47574f2c5eb723e52f88cd58a7e0a4 /ipalib/plugins
parent85d5bfd1b19b0ed6282a8c6cc056e8e550dde79d (diff)
downloadfreeipa-4ad8055341b9f12c833abdf757755ed95f1b375e.tar.gz
freeipa-4ad8055341b9f12c833abdf757755ed95f1b375e.tar.xz
freeipa-4ad8055341b9f12c833abdf757755ed95f1b375e.zip
Re-implement access control using an updated model.
The new model is based on permssions, privileges and roles. Most importantly it corrects the reverse membership that caused problems in the previous implementation. You add permission to privileges and privileges to roles, not the other way around (even though it works that way behind the scenes). A permission object is a combination of a simple group and an aci. The linkage between the aci and the permission is the description of the permission. This shows as the name/description of the aci. ldap:///self and groups granting groups (v1-style) are not supported by this model (it will be provided separately). This makes the aci plugin internal only. ticket 445
Diffstat (limited to 'ipalib/plugins')
-rw-r--r--ipalib/plugins/aci.py179
-rw-r--r--ipalib/plugins/baseldap.py264
-rw-r--r--ipalib/plugins/group.py4
-rw-r--r--ipalib/plugins/host.py6
-rw-r--r--ipalib/plugins/permission.py363
-rw-r--r--ipalib/plugins/privilege.py191
-rw-r--r--ipalib/plugins/role.py212
-rw-r--r--ipalib/plugins/rolegroup.py165
-rw-r--r--ipalib/plugins/service.py2
-rw-r--r--ipalib/plugins/taskgroup.py136
-rw-r--r--ipalib/plugins/user.py2
11 files changed, 1169 insertions, 355 deletions
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'