summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/constants.py5
-rw-r--r--ipalib/errors.py8
-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
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'