2010-12-01 10:23:52 -06:00
|
|
|
# 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)'),
|
2010-12-17 14:04:47 -06:00
|
|
|
values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dns',),
|
2010-12-01 10:23:52 -06:00
|
|
|
),
|
|
|
|
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):
|
2010-12-10 21:52:44 -06:00
|
|
|
try:
|
|
|
|
(dn, attrs) = ldap.get_entry(
|
|
|
|
dn, attrs_list, normalize=self.obj.normalize_dn
|
|
|
|
)
|
|
|
|
except errors.NotFound:
|
|
|
|
self.obj.handle_not_found(*keys)
|
2010-12-01 10:23:52 -06:00
|
|
|
opts = copy.copy(options)
|
|
|
|
if 'description' in opts:
|
|
|
|
del opts['description']
|
2010-12-10 21:52:44 -06:00
|
|
|
for o in ['all', 'raw', 'rights', 'description']:
|
2010-12-01 10:23:52 -06:00
|
|
|
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:
|
2010-12-10 21:52:44 -06:00
|
|
|
if attrs['description'][0] != options['description']:
|
|
|
|
self.api.Command.aci_rename(attrs['description'][0], newname=options['description'])
|
2010-12-01 10:23:52 -06:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2010-12-10 21:52:44 -06:00
|
|
|
if len(opts) > 0 and not aciupdate:
|
2010-12-01 10:23:52 -06:00
|
|
|
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:
|
2010-12-08 12:33:40 -06:00
|
|
|
(dn, attrs) = entry
|
|
|
|
if aci['permission'] == attrs['cn']:
|
2010-12-01 10:23:52 -06:00
|
|
|
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)
|