2009-06-10 07:24:35 -05:00
# Authors:
# Rob Crittenden <rcritten@redhat.com>
# Pavel Zuna <pzuna@redhat.com>
#
# Copyright (C) 2009 Red Hat
# see file 'COPYING' for use and warranty information
#
2010-12-09 06:59:11 -06:00
# 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, either version 3 of the License, or
# (at your option) any later version.
2009-06-10 07:24:35 -05:00
#
# 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
2010-12-09 06:59:11 -06:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2009-06-10 07:24:35 -05:00
"""
Directory Server Access Control Instructions ( ACIs )
2009-09-28 09:13:06 -05:00
2010-08-24 22:40:32 -05:00
ACIs are used to allow or deny access to information . This module is
currently designed to allow , not deny , access .
2009-09-28 09:13:06 -05:00
2010-08-24 22:40:32 -05:00
The aci commands are designed to grant permissions that allow updating
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
2010-12-01 10:23:52 -06:00
roles are another type of group , roles .
2009-09-28 09:13:06 -05:00
For example , if you have taskgroups that allow adding and modifying users you
2010-12-01 10:23:52 -06:00
could create a role , useradmin . You would assign users to the useradmin
role to allow them to do the operations defined by the taskgroups .
2009-09-28 09:13:06 -05:00
2010-08-24 22:40:32 -05:00
You can create ACIs that delegate permission so users in group A can write
attributes on group B .
2009-09-28 09:13:06 -05:00
The type option is a map that applies to all entries in the users , groups or
host location . It is primarily designed to be used when granting add
permissions ( to write new entries ) .
2010-08-24 22:40:32 -05:00
An ACI consists of three parts :
1. target
2. permissions
3. bind rules
The target is a set of rules that define which LDAP objects are being
2010-12-02 12:25:00 -06:00
targeted . This can include a list of attributes , an area of that LDAP
2010-08-24 22:40:32 -05:00
tree or an LDAP filter .
2010-12-02 12:25:00 -06:00
The targets include :
- attrs : list of attributes affected
- type : an object type ( user , group , host , service , etc )
- memberof : members of a group
- targetgroup : grant access to modify a specific group . This is primarily
designed to enable users to add or remove members of a specific group .
- filter : A legal LDAP filter used to narrow the scope of the target .
- subtree : Used to apply a rule across an entire set of objects . For example ,
to allow adding users you need to grant " add " permission to the subtree
ldap : / / uid = * , cn = users , cn = accounts , dc = example , dc = com . The subtree option
is a fail - safe for objects that may not be covered by the type option .
2011-09-05 09:30:05 -05:00
The permissions define what the ACI is allowed to do , and are one or
2010-12-02 12:25:00 -06:00
more of :
2010-08-24 22:40:32 -05:00
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
2011-05-04 03:26:18 -05:00
be editable .
2010-08-24 22:40:32 -05:00
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
2010-12-01 10:23:52 -06:00
taskgroups so that the rights can be easily shared through roles .
2010-08-24 22:40:32 -05:00
2009-09-28 09:13:06 -05:00
For a more thorough description of access controls see
http : / / www . redhat . com / docs / manuals / dir - server / ag / 8.0 / Managing_Access_Control . html
EXAMPLES :
2011-05-04 03:26:18 -05:00
NOTE : ACIs are now added via the permission plugin . These examples are to
2010-12-02 12:25:00 -06:00
demonstrate how the various options work but this is done via the permission
command - line now ( see last example ) .
2010-08-24 22:40:32 -05:00
Add an ACI so that the group " secretaries " can update the address on any user :
2010-12-02 12:25:00 -06:00
ipa group - add - - desc = " Office secretaries " secretaries
2011-01-21 02:20:01 -06:00
ipa aci - add - - attrs = streetAddress - - memberof = ipausers - - group = secretaries - - permissions = write - - prefix = none " Secretaries write addresses "
2009-09-28 09:13:06 -05:00
Show the new ACI :
2011-01-21 02:20:01 -06:00
ipa aci - show - - prefix = none " Secretaries write addresses "
2009-09-28 09:13:06 -05:00
2010-12-02 12:25:00 -06:00
Add an ACI that allows members of the " addusers " permission to add new users :
2011-01-21 02:20:01 -06:00
ipa aci - add - - type = user - - permission = addusers - - permissions = add - - prefix = none " Add new users "
2009-09-28 09:13:06 -05:00
2010-12-02 12:25:00 -06:00
Add an ACI that allows members of the editors manage members of the admins group :
2011-01-21 02:20:01 -06:00
ipa aci - add - - permissions = write - - attrs = member - - targetgroup = admins - - group = editors - - prefix = none " Editors manage admins "
2010-12-01 10:23:52 -06:00
2011-09-05 09:30:05 -05:00
Add an ACI that allows members of the admins group to manage the street and zip code of those in the editors group :
2011-01-21 02:20:01 -06:00
ipa aci - add - - permissions = write - - memberof = editors - - group = admins - - attrs = street , postalcode - - prefix = none " admins edit the address of editors "
2010-12-02 12:25:00 -06:00
Add an ACI that allows the admins group manage the street and zipcode of those who work for the boss :
2011-01-21 02:20:01 -06:00
ipa aci - add - - permissions = write - - group = admins - - attrs = street , postalcode - - filter = " (manager=uid=boss,cn=users,cn=accounts,dc=example,dc=com) " - - prefix = none " Edit the address of those who work for the boss "
2010-12-02 12:25:00 -06:00
Add an entirely new kind of record to IPA that isn ' t covered by any of the --type options, creating a permission:
ipa permission - add - - permissions = add - - subtree = " cn=*,cn=orange,cn=accounts,dc=example,dc=com " - - desc = " Add Orange Entries " add_orange
2010-08-24 22:40:32 -05:00
The show command shows the raw 389 - ds ACI .
2009-09-28 09:13:06 -05:00
2010-08-10 12:22:50 -05:00
IMPORTANT : When modifying the target attributes of an existing ACI you
must include all existing attributes as well . When doing an aci - mod the
targetattr REPLACES the current attributes , it does not add to them .
2009-06-10 07:24:35 -05:00
"""
2012-01-06 06:58:01 -06:00
from copy import deepcopy
2009-06-10 07:24:35 -05:00
from ipalib import api , crud , errors
from ipalib import Object , Command
2011-11-21 09:50:27 -06:00
from ipalib import Flag , Int , Str , StrEnum
2009-06-10 07:24:35 -05:00
from ipalib . aci import ACI
2011-12-06 17:15:41 -06:00
from ipalib . dn import DN
2009-12-11 16:35:06 -06:00
from ipalib import output
from ipalib import _ , ngettext
2010-12-10 12:31:58 -06:00
if api . env . in_server and api . env . context in [ ' lite ' , ' server ' ] :
from ldap import explode_dn
2011-11-15 13:39:31 -06:00
from ipapython . ipa_log_manager import *
2009-06-10 07:24:35 -05:00
2011-01-21 02:20:01 -06:00
ACI_NAME_PREFIX_SEP = " : "
2009-06-10 07:24:35 -05:00
_type_map = {
' user ' : ' ldap:///uid=*, %s , %s ' % ( api . env . container_user , api . env . basedn ) ,
' group ' : ' ldap:///cn=*, %s , %s ' % ( api . env . container_group , api . env . basedn ) ,
2010-11-03 10:30:03 -05:00
' host ' : ' ldap:///fqdn=*, %s , %s ' % ( api . env . container_host , api . env . basedn ) ,
' hostgroup ' : ' ldap:///cn=*, %s , %s ' % ( api . env . container_hostgroup , api . env . basedn ) ,
' service ' : ' ldap:///krbprincipalname=*, %s , %s ' % ( api . env . container_service , api . env . basedn ) ,
' netgroup ' : ' ldap:///ipauniqueid=*, %s , %s ' % ( api . env . container_netgroup , api . env . basedn ) ,
2011-01-13 10:32:57 -06:00
' dnsrecord ' : ' ldap:///idnsname=*, %s , %s ' % ( api . env . container_dns , api . env . basedn ) ,
2009-06-10 07:24:35 -05:00
}
_valid_permissions_values = [
2010-08-24 22:40:32 -05:00
u ' read ' , u ' write ' , u ' add ' , u ' delete ' , u ' all '
2009-06-10 07:24:35 -05:00
]
2011-01-21 02:20:01 -06:00
_valid_prefix_values = (
u ' permission ' , u ' delegation ' , u ' selfservice ' , u ' none '
)
2009-12-11 16:35:06 -06:00
class ListOfACI ( output . Output ) :
type = ( list , tuple )
2010-03-05 15:11:21 -06:00
doc = _ ( ' A list of ACI values ' )
2009-12-11 16:35:06 -06:00
def validate ( self , cmd , entries ) :
assert isinstance ( entries , self . type )
for ( i , entry ) in enumerate ( entries ) :
if not isinstance ( entry , unicode ) :
raise TypeError ( output . emsg %
( cmd . name , self . __class__ . __name__ ,
self . name , i , unicode , type ( entry ) , entry )
)
aci_output = (
output . Output ( ' result ' , unicode , ' A string representing the ACI ' ) ,
output . value ,
output . summary ,
)
2011-01-21 02:20:01 -06:00
def _make_aci_name ( aciprefix , aciname ) :
"""
Given a name and a prefix construct an ACI name .
"""
if aciprefix == u " none " :
return aciname
return aciprefix + ACI_NAME_PREFIX_SEP + aciname
def _parse_aci_name ( aciname ) :
"""
Parse the raw ACI name and return a tuple containing the ACI prefix
and the actual ACI name .
"""
aciparts = aciname . partition ( ACI_NAME_PREFIX_SEP )
if not aciparts [ 2 ] : # no prefix/name separator found
return ( u " none " , aciparts [ 0 ] )
return ( aciparts [ 0 ] , aciparts [ 2 ] )
2009-06-10 07:24:35 -05:00
2011-01-31 12:10:37 -06:00
def _group_from_memberof ( memberof ) :
"""
Pull the group name out of a memberOf filter
"""
st = memberof . find ( ' memberOf= ' )
if st == - 1 :
# We have a raw group name, use that
return api . Object [ ' group ' ] . get_dn ( memberof )
en = memberof . find ( ' ) ' , st )
return memberof [ st + 9 : en ]
2011-01-20 15:35:34 -06:00
def _make_aci ( ldap , current , aciname , kw ) :
2010-08-10 12:22:50 -05:00
"""
Given a name and a set of keywords construct an ACI .
"""
# Do some quick and dirty validation.
2009-09-28 09:13:06 -05:00
t1 = ' type ' in kw
t2 = ' filter ' in kw
t3 = ' subtree ' in kw
t4 = ' targetgroup ' in kw
t5 = ' attrs ' in kw
t6 = ' memberof ' in kw
if t1 + t2 + t3 + t4 > 1 :
2009-12-11 16:35:06 -06:00
raise errors . ValidationError ( name = ' target ' , error = _ ( ' type, filter, subtree and targetgroup are mutually exclusive ' ) )
2009-09-28 09:13:06 -05:00
2011-01-21 02:20:01 -06:00
if ' aciprefix ' not in kw :
raise errors . ValidationError ( name = ' aciprefix ' , error = _ ( ' ACI prefix is required ' ) )
2009-09-28 09:13:06 -05:00
if t1 + t2 + t3 + t4 + t5 + t6 == 0 :
2009-12-11 16:35:06 -06:00
raise errors . ValidationError ( name = ' target ' , error = _ ( ' at least one of: type, filter, subtree, targetgroup, attrs or memberof are required ' ) )
2009-09-28 09:13:06 -05:00
2011-01-31 12:10:37 -06:00
if t2 + t6 > 1 :
raise errors . ValidationError ( name = ' target ' , error = _ ( ' filter and memberof are mutually exclusive ' ) )
2009-09-28 09:13:06 -05:00
group = ' group ' in kw
2010-12-01 10:23:52 -06:00
permission = ' permission ' in kw
2010-08-10 12:22:50 -05:00
selfaci = ' selfaci ' in kw and kw [ ' selfaci ' ] == True
2010-12-01 10:23:52 -06:00
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 ' ) )
2009-09-28 09:13:06 -05:00
# Grab the dn of the group we're granting access to. This group may be a
2010-12-01 10:23:52 -06:00
# permission or a user group.
2009-12-11 16:35:06 -06:00
entry_attrs = [ ]
2010-12-01 10:23:52 -06:00
if permission :
# This will raise NotFound if the permission doesn't exist
2009-09-28 09:13:06 -05:00
try :
2010-12-01 10:23:52 -06:00
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 ) }
2009-09-28 09:13:06 -05:00
elif group :
# Not so friendly with groups. This will raise
try :
2009-12-11 16:35:06 -06:00
entry_attrs = api . Command [ ' group_show ' ] ( kw [ ' group ' ] ) [ ' result ' ]
2009-09-28 09:13:06 -05:00
except errors . NotFound :
2009-12-11 16:35:06 -06:00
raise errors . NotFound ( reason = _ ( " Group ' %s ' does not exist " ) % kw [ ' group ' ] )
2009-06-10 07:24:35 -05:00
2010-12-21 21:39:55 -06:00
try :
a = ACI ( current )
2011-01-21 02:20:01 -06:00
a . name = _make_aci_name ( kw [ ' aciprefix ' ] , aciname )
2010-12-21 21:39:55 -06:00
a . permissions = kw [ ' permissions ' ]
if ' selfaci ' in kw and kw [ ' selfaci ' ] :
a . set_bindrule ( ' userdn = " ldap:///self " ' )
else :
dn = entry_attrs [ ' dn ' ]
a . set_bindrule ( ' groupdn = " ldap:/// %s " ' % dn )
if ' attrs ' in kw :
a . set_target_attr ( kw [ ' attrs ' ] )
if ' memberof ' in kw :
2011-01-31 12:10:37 -06:00
groupdn = _group_from_memberof ( kw [ ' memberof ' ] )
a . set_target_filter ( ' memberOf= %s ' % groupdn )
2010-12-21 21:39:55 -06:00
if ' filter ' in kw :
2011-01-20 15:35:34 -06:00
# Test the filter by performing a simple search on it. The
# filter is considered valid if either it returns some entries
# or it returns no entries, otherwise we let whatever exception
# happened be raised.
if kw [ ' filter ' ] in ( ' ' , None , u ' ' ) :
raise errors . BadSearchFilter ( info = _ ( ' empty filter ' ) )
try :
entries = ldap . find_entries ( filter = kw [ ' filter ' ] )
except errors . NotFound :
pass
2010-12-21 21:39:55 -06:00
a . set_target_filter ( kw [ ' filter ' ] )
if ' type ' in kw :
target = _type_map [ kw [ ' type ' ] ]
a . set_target ( target )
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 ' % entry_attrs [ ' dn ' ]
a . set_target ( target )
if ' subtree ' in kw :
# See if the subtree is a full URI
target = kw [ ' subtree ' ]
if not target . startswith ( ' ldap:/// ' ) :
target = ' ldap:/// %s ' % target
a . set_target ( target )
except SyntaxError , e :
raise errors . ValidationError ( name = ' target ' , error = _ ( ' Syntax Error: %(error)s ' ) % dict ( error = str ( e ) ) )
2009-06-10 07:24:35 -05:00
return a
2010-12-01 10:23:52 -06:00
def _aci_to_kw ( ldap , a , test = False ) :
2010-06-11 11:11:24 -05:00
""" Convert an ACI into its equivalent keywords.
This is used for the modify operation so we can merge the
incoming kw and existing ACI and pass the result to
_make_aci ( ) .
"""
kw = { }
2011-01-21 02:20:01 -06:00
kw [ ' aciprefix ' ] , kw [ ' aciname ' ] = _parse_aci_name ( a . name )
2010-06-11 11:11:24 -05:00
kw [ ' permissions ' ] = tuple ( a . permissions )
if ' targetattr ' in a . target :
2010-11-03 10:30:03 -05:00
kw [ ' attrs ' ] = list ( a . target [ ' targetattr ' ] [ ' expression ' ] )
for i in xrange ( len ( kw [ ' attrs ' ] ) ) :
kw [ ' attrs ' ] [ i ] = unicode ( kw [ ' attrs ' ] [ i ] )
kw [ ' attrs ' ] = tuple ( kw [ ' attrs ' ] )
2010-06-11 11:11:24 -05:00
if ' targetfilter ' in a . target :
target = a . target [ ' targetfilter ' ] [ ' expression ' ]
2011-12-06 17:15:41 -06:00
if target . startswith ( ' (memberOf= ' ) or target . startswith ( ' memberOf= ' ) :
( junk , memberof ) = target . split ( ' memberOf= ' , 1 )
memberof = DN ( memberof )
kw [ ' memberof ' ] = memberof [ ' cn ' ]
2010-06-11 11:11:24 -05:00
else :
2010-11-03 10:30:03 -05:00
kw [ ' filter ' ] = unicode ( target )
2010-06-11 11:11:24 -05:00
if ' target ' in a . target :
target = a . target [ ' target ' ] [ ' expression ' ]
found = False
for k in _type_map . keys ( ) :
if _type_map [ k ] == target :
kw [ ' type ' ] = unicode ( k )
found = True
break ;
if not found :
if target . startswith ( ' ( ' ) :
2010-11-03 10:30:03 -05:00
kw [ ' filter ' ] = unicode ( target )
2010-06-11 11:11:24 -05:00
else :
# See if the target is a group. If so we set the
# targetgroup attr, otherwise we consider it a subtree
if api . env . container_group in target :
2010-12-10 12:31:58 -06:00
targetdn = unicode ( target . replace ( ' ldap:/// ' , ' ' ) )
2011-12-06 17:15:41 -06:00
target = DN ( targetdn )
kw [ ' targetgroup ' ] = target [ ' cn ' ]
2010-06-11 11:11:24 -05:00
else :
2010-11-03 10:30:03 -05:00
kw [ ' subtree ' ] = unicode ( target )
2010-06-11 11:11:24 -05:00
groupdn = a . bindrule [ ' expression ' ]
groupdn = groupdn . replace ( ' ldap:/// ' , ' ' )
2010-08-10 12:22:50 -05:00
if groupdn == ' self ' :
kw [ ' selfaci ' ] = True
2010-11-03 10:30:03 -05:00
elif groupdn == ' anyone ' :
pass
2010-06-11 11:11:24 -05:00
else :
2010-11-03 10:30:03 -05:00
if groupdn . startswith ( ' cn= ' ) :
2010-12-01 10:23:52 -06:00
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 ]
2010-11-03 10:30:03 -05:00
else :
2010-12-01 10:23:52 -06:00
if ' cn ' in entry_attrs :
kw [ ' group ' ] = entry_attrs [ ' cn ' ] [ 0 ]
2010-06-11 11:11:24 -05:00
return kw
2009-06-10 07:24:35 -05:00
def _convert_strings_to_acis ( acistrs ) :
acis = [ ]
for a in acistrs :
try :
acis . append ( ACI ( a ) )
except SyntaxError , e :
2011-11-15 13:39:31 -06:00
root_logger . warning ( " Failed to parse: %s " % a )
2009-06-10 07:24:35 -05:00
return acis
2011-01-21 02:20:01 -06:00
def _find_aci_by_name ( acis , aciprefix , aciname ) :
name = _make_aci_name ( aciprefix , aciname ) . lower ( )
2009-06-10 07:24:35 -05:00
for a in acis :
2011-01-21 02:20:01 -06:00
if a . name . lower ( ) == name :
2009-06-10 07:24:35 -05:00
return a
2009-12-11 16:35:06 -06:00
raise errors . NotFound ( reason = _ ( ' ACI with name " %s " not found ' ) % aciname )
2009-06-10 07:24:35 -05:00
2010-11-03 10:30:03 -05:00
def validate_permissions ( ugettext , permissions ) :
valid_permissions = [ ]
permissions = permissions . split ( ' , ' )
for p in permissions :
p = p . strip ( ) . lower ( )
if not p in _valid_permissions_values :
return ' " %s " is not a valid permission ' % p
2009-06-10 07:24:35 -05:00
def _normalize_permissions ( permissions ) :
valid_permissions = [ ]
permissions = permissions . split ( ' , ' )
for p in permissions :
p = p . strip ( ) . lower ( )
2010-11-03 10:30:03 -05:00
if p not in valid_permissions :
2009-06-10 07:24:35 -05:00
valid_permissions . append ( p )
return ' , ' . join ( valid_permissions )
2011-01-21 02:20:01 -06:00
_prefix_option = StrEnum ( ' aciprefix ' ,
cli_name = ' prefix ' ,
label = _ ( ' ACI prefix ' ) ,
doc = _ ( ' Prefix used to distinguish ACI types ' \
' (permission, delegation, selfservice, none) ' ) ,
values = _valid_prefix_values ,
)
2009-06-10 07:24:35 -05:00
2009-06-16 07:38:27 -05:00
class aci ( Object ) :
2009-06-10 07:24:35 -05:00
"""
ACI object .
"""
2011-01-20 14:07:43 -06:00
NO_CLI = True
2010-02-08 06:03:28 -06:00
label = _ ( ' ACIs ' )
2009-06-10 07:24:35 -05:00
takes_params = (
Str ( ' aciname ' ,
cli_name = ' name ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' ACI name ' ) ,
2009-06-10 07:24:35 -05:00
primary_key = True ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
2010-12-01 10:23:52 -06:00
Str ( ' permission? ' ,
cli_name = ' permission ' ,
label = _ ( ' Permission ' ) ,
doc = _ ( ' Permission ACI grants access to ' ) ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
2009-09-28 09:13:06 -05:00
Str ( ' group? ' ,
cli_name = ' group ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' User group ' ) ,
doc = _ ( ' User group ACI grants access to ' ) ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-09-28 09:13:06 -05:00
) ,
2011-11-21 09:50:27 -06:00
Str ( ' permissions+ ' , validate_permissions ,
2009-06-10 07:24:35 -05:00
cli_name = ' permissions ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' Permissions ' ) ,
doc = _ ( ' comma-separated list of permissions to grant ' \
2010-08-24 22:40:32 -05:00
' (read, write, add, delete, all) ' ) ,
2011-11-21 09:50:27 -06:00
csv = True ,
2009-06-10 07:24:35 -05:00
normalizer = _normalize_permissions ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
2011-11-21 09:50:27 -06:00
Str ( ' attrs* ' ,
2009-06-10 07:24:35 -05:00
cli_name = ' attrs ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' Attributes ' ) ,
doc = _ ( ' Comma-separated list of attributes ' ) ,
2011-11-21 09:50:27 -06:00
csv = True ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
StrEnum ( ' type? ' ,
cli_name = ' type ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' Type ' ) ,
2010-11-03 10:30:03 -05:00
doc = _ ( ' type of IPA object (user, group, host, hostgroup, service, netgroup) ' ) ,
2011-01-13 10:32:57 -06:00
values = ( u ' user ' , u ' group ' , u ' host ' , u ' service ' , u ' hostgroup ' , u ' netgroup ' , u ' dnsrecord ' ) ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
Str ( ' memberof? ' ,
cli_name = ' memberof ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' Member of ' ) , # FIXME: Does this label make sense?
doc = _ ( ' Member of a group ' ) ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
Str ( ' filter? ' ,
cli_name = ' filter ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' Filter ' ) ,
doc = _ ( ' Legal LDAP filter (e.g. ou=Engineering) ' ) ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
Str ( ' subtree? ' ,
cli_name = ' subtree ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' Subtree ' ) ,
doc = _ ( ' Subtree to apply ACI to ' ) ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
Str ( ' targetgroup? ' ,
cli_name = ' targetgroup ' ,
2010-02-19 10:08:16 -06:00
label = _ ( ' Target group ' ) ,
doc = _ ( ' Group to apply ACI to ' ) ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2009-06-10 07:24:35 -05:00
) ,
2010-08-10 12:22:50 -05:00
Flag ( ' selfaci? ' ,
cli_name = ' self ' ,
label = _ ( ' Target your own entry (self) ' ) ,
doc = _ ( ' Apply ACI to your own entry (self) ' ) ,
2011-11-14 10:03:44 -06:00
flags = ( ' virtual_attribute ' , ) ,
2010-08-10 12:22:50 -05:00
) ,
2009-06-10 07:24:35 -05:00
)
2009-06-16 07:38:27 -05:00
api . register ( aci )
2009-06-10 07:24:35 -05:00
2009-06-16 09:51:44 -05:00
class aci_add ( crud . Create ) :
2009-06-10 07:24:35 -05:00
"""
Create new ACI .
"""
2011-01-20 14:07:43 -06:00
NO_CLI = True
2009-12-11 16:35:06 -06:00
msg_summary = _ ( ' Created ACI " %(value)s " ' )
2010-12-01 10:23:52 -06:00
takes_options = (
2011-01-21 02:20:01 -06:00
_prefix_option ,
2010-12-01 10:23:52 -06:00
Flag ( ' test? ' ,
doc = _ ( ' Test the ACI syntax but don \' t write anything ' ) ,
default = False ,
) ,
)
2009-06-10 07:24:35 -05:00
def execute ( self , aciname , * * kw ) :
"""
Execute the aci - create operation .
Returns the entry as it will be created in LDAP .
: param aciname : The name of the ACI being added .
: param kw : Keyword arguments for the other LDAP attributes .
"""
assert ' aciname ' not in kw
ldap = self . api . Backend . ldap2
2011-01-20 15:35:34 -06:00
newaci = _make_aci ( ldap , None , aciname , kw )
2009-06-10 07:24:35 -05:00
( dn , entry_attrs ) = ldap . get_entry ( self . api . env . basedn , [ ' aci ' ] )
acis = _convert_strings_to_acis ( entry_attrs . get ( ' aci ' , [ ] ) )
for a in acis :
2010-12-01 10:23:52 -06:00
# FIXME: add check for permission_group = permission_group
if a . isequal ( newaci ) or newaci . name == a . name :
2009-06-10 07:24:35 -05:00
raise errors . DuplicateEntry ( )
2009-12-11 16:35:06 -06:00
newaci_str = unicode ( newaci )
2009-09-28 09:13:06 -05:00
entry_attrs [ ' aci ' ] . append ( newaci_str )
2009-06-10 07:24:35 -05:00
2010-12-01 10:23:52 -06:00
if not kw . get ( ' test ' , False ) :
ldap . update_entry ( dn , entry_attrs )
2009-06-10 07:24:35 -05:00
2010-11-03 10:30:03 -05:00
if kw . get ( ' raw ' , False ) :
result = dict ( aci = unicode ( newaci_str ) )
else :
2010-12-01 10:23:52 -06:00
result = _aci_to_kw ( ldap , newaci , kw . get ( ' test ' , False ) )
2009-12-11 16:35:06 -06:00
return dict (
2010-11-03 10:30:03 -05:00
result = result ,
value = aciname ,
2009-12-11 16:35:06 -06:00
)
2009-06-10 07:24:35 -05:00
2009-06-16 09:51:44 -05:00
api . register ( aci_add )
2009-06-10 07:24:35 -05:00
2009-06-16 09:51:44 -05:00
class aci_del ( crud . Delete ) :
2009-06-10 07:24:35 -05:00
"""
Delete ACI .
"""
2011-01-20 14:07:43 -06:00
NO_CLI = True
2011-01-07 10:17:55 -06:00
has_output = output . standard_boolean
2009-12-11 16:35:06 -06:00
msg_summary = _ ( ' Deleted ACI " %(value)s " ' )
2011-01-21 02:20:01 -06:00
takes_options = ( _prefix_option , )
2009-06-10 07:24:35 -05:00
def execute ( self , aciname , * * kw ) :
"""
Execute the aci - delete operation .
2011-11-10 18:53:21 -06:00
: param aciname : The name of the ACI being deleted .
2009-06-10 07:24:35 -05:00
: param kw : unused
"""
assert ' aciname ' not in kw
ldap = self . api . Backend . ldap2
( dn , entry_attrs ) = ldap . get_entry ( self . api . env . basedn , [ ' aci ' ] )
acistrs = entry_attrs . get ( ' aci ' , [ ] )
acis = _convert_strings_to_acis ( acistrs )
2011-01-21 02:20:01 -06:00
aci = _find_aci_by_name ( acis , kw [ ' aciprefix ' ] , aciname )
2009-06-10 07:24:35 -05:00
for a in acistrs :
2009-09-28 09:13:06 -05:00
candidate = ACI ( a )
if aci . isequal ( candidate ) :
2009-06-10 07:24:35 -05:00
acistrs . remove ( a )
break
entry_attrs [ ' aci ' ] = acistrs
ldap . update_entry ( dn , entry_attrs )
2009-12-11 16:35:06 -06:00
return dict (
result = True ,
value = aciname ,
)
2009-06-10 07:24:35 -05:00
2009-06-16 09:51:44 -05:00
api . register ( aci_del )
2009-06-10 07:24:35 -05:00
2009-06-16 07:38:27 -05:00
class aci_mod ( crud . Update ) :
2009-06-10 07:24:35 -05:00
"""
Modify ACI .
"""
2011-01-20 14:07:43 -06:00
NO_CLI = True
2010-11-03 10:30:03 -05:00
has_output_params = (
Str ( ' aci ' ,
label = _ ( ' ACI ' ) ,
) ,
)
2011-01-21 02:20:01 -06:00
takes_options = ( _prefix_option , )
2009-12-11 16:35:06 -06:00
msg_summary = _ ( ' Modified ACI " %(value)s " ' )
2009-06-10 07:24:35 -05:00
def execute ( self , aciname , * * kw ) :
ldap = self . api . Backend . ldap2
2009-09-28 09:13:06 -05:00
2009-06-10 07:24:35 -05:00
( dn , entry_attrs ) = ldap . get_entry ( self . api . env . basedn , [ ' aci ' ] )
acis = _convert_strings_to_acis ( entry_attrs . get ( ' aci ' , [ ] ) )
2011-01-21 02:20:01 -06:00
aci = _find_aci_by_name ( acis , kw [ ' aciprefix ' ] , aciname )
2009-06-10 07:24:35 -05:00
2010-06-11 11:11:24 -05:00
# 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.
2012-01-06 06:58:01 -06:00
oldkw = _aci_to_kw ( ldap , aci )
newkw = deepcopy ( oldkw )
2010-08-10 12:22:50 -05:00
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
2010-06-11 11:11:24 -05:00
for k in kw . keys ( ) :
newkw [ k ] = kw [ k ]
2012-01-06 06:58:01 -06:00
for acikw in ( oldkw , newkw ) :
try :
del acikw [ ' aciname ' ]
except KeyError :
pass
2009-06-10 07:24:35 -05:00
2010-08-10 12:22:50 -05:00
# _make_aci is what is run in aci_add and validates the input.
# Do this before we delete the existing ACI.
2011-01-20 15:35:34 -06:00
newaci = _make_aci ( ldap , None , aciname , newkw )
2010-12-01 10:23:52 -06:00
if aci . isequal ( newaci ) :
raise errors . EmptyModlist ( )
2010-08-10 12:22:50 -05:00
2011-01-21 02:20:01 -06:00
self . api . Command [ ' aci_del ' ] ( aciname , * * kw )
2009-06-10 07:24:35 -05:00
2012-01-06 06:58:01 -06:00
try :
result = self . api . Command [ ' aci_add ' ] ( aciname , * * newkw ) [ ' result ' ]
except Exception , e :
# ACI could not be added, try to restore the old deleted ACI and
# report the ADD error back to user
try :
self . api . Command [ ' aci_add ' ] ( aciname , * * oldkw )
except :
pass
raise e
2010-06-11 11:11:24 -05:00
2010-11-03 10:30:03 -05:00
if kw . get ( ' raw ' , False ) :
result = dict ( aci = unicode ( newaci ) )
else :
result = _aci_to_kw ( ldap , newaci )
2010-06-11 11:11:24 -05:00
return dict (
result = result ,
value = aciname ,
)
2009-06-10 07:24:35 -05:00
2009-06-16 07:38:27 -05:00
api . register ( aci_mod )
2009-06-10 07:24:35 -05:00
2009-06-16 07:38:27 -05:00
class aci_find ( crud . Search ) :
2009-06-10 07:24:35 -05:00
"""
Search for ACIs .
2009-09-28 09:13:06 -05:00
Returns a list of ACIs
EXAMPLES :
To find all ACIs that apply directly to members of the group ipausers :
ipa aci - find - - memberof = ipausers
To find all ACIs that grant add access :
ipa aci - find - - permissions = add
Note that the find command only looks for the given text in the set of
ACIs , it does not evaluate the ACIs to see if something would apply .
For example , searching on memberof = ipausers will find all ACIs that
have ipausers as a memberof . There may be other ACIs that apply to
members of that group indirectly .
2009-06-10 07:24:35 -05:00
"""
2011-01-20 14:07:43 -06:00
NO_CLI = True
2009-12-11 16:35:06 -06:00
msg_summary = ngettext ( ' %(count)d ACI matched ' , ' %(count)d ACIs matched ' , 0 )
2011-01-21 02:20:01 -06:00
takes_options = ( _prefix_option . clone_rename ( " aciprefix? " , required = False ) , )
2009-06-10 07:24:35 -05:00
def execute ( self , term , * * 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 ' , [ ] ) )
results = [ ]
if term :
term = term . lower ( )
for a in acis :
if a . name . lower ( ) . find ( term ) != - 1 and a not in results :
results . append ( a )
acis = list ( results )
else :
results = list ( acis )
if ' aciname ' in kw :
for a in acis :
2011-01-21 02:20:01 -06:00
prefix , name = _parse_aci_name ( a . name )
if name != kw [ ' aciname ' ] :
results . remove ( a )
acis = list ( results )
if ' aciprefix ' in kw :
for a in acis :
prefix , name = _parse_aci_name ( a . name )
if prefix != kw [ ' aciprefix ' ] :
2009-06-10 07:24:35 -05:00
results . remove ( a )
acis = list ( results )
if ' attrs ' in kw :
for a in acis :
2010-11-03 10:30:03 -05:00
if not ' targetattr ' in a . target :
results . remove ( a )
continue
2009-06-10 07:24:35 -05:00
alist1 = sorted (
[ t . lower ( ) for t in a . target [ ' targetattr ' ] [ ' expression ' ] ]
)
alist2 = sorted ( [ t . lower ( ) for t in kw [ ' attrs ' ] ] )
2010-11-03 10:30:03 -05:00
if len ( set ( alist1 ) & set ( alist2 ) ) != len ( alist2 ) :
2009-06-10 07:24:35 -05:00
results . remove ( a )
acis = list ( results )
2010-12-01 10:23:52 -06:00
if ' permission ' in kw :
2009-06-10 07:24:35 -05:00
try :
2010-12-01 10:23:52 -06:00
self . api . Command [ ' permission_show ' ] (
kw [ ' permission ' ]
2010-06-11 11:11:24 -05:00
)
2009-06-10 07:24:35 -05:00
except errors . NotFound :
pass
else :
for a in acis :
if a . bindrule [ ' expression ' ] != ( ' ldap:/// %s ' % dn ) :
results . remove ( a )
acis = list ( results )
if ' permissions ' in kw :
for a in acis :
alist1 = sorted ( a . permissions )
alist2 = sorted ( kw [ ' permissions ' ] )
2010-11-03 10:30:03 -05:00
if len ( set ( alist1 ) & set ( alist2 ) ) != len ( alist2 ) :
2009-06-10 07:24:35 -05:00
results . remove ( a )
2010-11-03 10:30:03 -05:00
acis = list ( results )
2009-06-10 07:24:35 -05:00
if ' memberof ' in kw :
try :
2011-01-31 12:10:37 -06:00
dn = _group_from_memberof ( kw [ ' memberof ' ] )
2009-06-10 07:24:35 -05:00
except errors . NotFound :
pass
else :
memberof_filter = ' (memberOf= %s ) ' % dn
for a in acis :
if ' targetfilter ' in a . target :
2009-09-28 09:13:06 -05:00
targetfilter = a . target [ ' targetfilter ' ] [ ' expression ' ]
if targetfilter != memberof_filter :
2009-06-10 07:24:35 -05:00
results . remove ( a )
else :
results . remove ( a )
2010-12-10 12:31:58 -06:00
if ' type ' in kw :
for a in acis :
2010-12-08 12:33:40 -06:00
if ' target ' in a . target :
target = a . target [ ' target ' ] [ ' expression ' ]
else :
2010-12-01 10:23:52 -06:00
results . remove ( a )
2010-12-08 12:33:40 -06:00
continue
found = False
for k in _type_map . keys ( ) :
if _type_map [ k ] == target and kw [ ' type ' ] == k :
found = True
break ;
if not found :
try :
results . remove ( a )
except ValueError :
pass
if ' selfaci ' in kw and kw [ ' selfaci ' ] == True :
for a in acis :
if a . bindrule [ ' expression ' ] != u ' ldap:///self ' :
try :
results . remove ( a )
except ValueError :
pass
2010-12-01 10:23:52 -06:00
2010-12-10 12:31:58 -06:00
if ' group ' in kw :
for a in acis :
groupdn = a . bindrule [ ' expression ' ]
groupdn = groupdn . replace ( ' ldap:/// ' , ' ' )
cn = None
if groupdn . startswith ( ' cn= ' ) :
cn = explode_dn ( groupdn ) [ 0 ]
cn = cn . replace ( ' cn= ' , ' ' )
if cn is None or cn != kw [ ' group ' ] :
try :
results . remove ( a )
except ValueError :
pass
if ' targetgroup ' in kw :
for a in acis :
found = False
if ' target ' in a . target :
target = a . target [ ' target ' ] [ ' expression ' ]
if api . env . container_group in target :
targetdn = unicode ( target . replace ( ' ldap:/// ' , ' ' ) )
cn = explode_dn ( targetdn ) [ 0 ]
cn = cn . replace ( ' cn= ' , ' ' )
if cn == kw [ ' targetgroup ' ] :
found = True
if not found :
try :
results . remove ( a )
except ValueError :
pass
2011-01-27 04:11:28 -06:00
if ' filter ' in kw :
if not kw [ ' filter ' ] . startswith ( ' ( ' ) :
kw [ ' filter ' ] = unicode ( ' ( ' + kw [ ' filter ' ] + ' ) ' )
for a in acis :
if ' targetfilter ' not in a . target or \
not a . target [ ' targetfilter ' ] [ ' expression ' ] or \
a . target [ ' targetfilter ' ] [ ' expression ' ] != kw [ ' filter ' ] :
results . remove ( a )
# TODO: searching by: subtree
2009-06-10 07:24:35 -05:00
2010-11-03 10:30:03 -05:00
acis = [ ]
for result in results :
if kw . get ( ' raw ' , False ) :
aci = dict ( aci = unicode ( result ) )
else :
aci = _aci_to_kw ( ldap , result )
acis . append ( aci )
2009-06-10 07:24:35 -05:00
2010-11-03 10:30:03 -05:00
return dict (
result = acis ,
count = len ( acis ) ,
truncated = False ,
2009-06-10 07:24:35 -05:00
)
2009-06-16 07:38:27 -05:00
api . register ( aci_find )
2009-06-10 07:24:35 -05:00
2009-06-16 07:38:27 -05:00
class aci_show ( crud . Retrieve ) :
2009-06-10 07:24:35 -05:00
"""
2009-09-28 09:13:06 -05:00
Display a single ACI given an ACI name .
2009-06-10 07:24:35 -05:00
"""
2011-01-20 14:07:43 -06:00
NO_CLI = True
2010-11-03 10:30:03 -05:00
has_output_params = (
Str ( ' aci ' ,
label = _ ( ' ACI ' ) ,
) ,
2009-12-11 16:35:06 -06:00
)
2011-01-21 02:20:01 -06:00
takes_options = ( _prefix_option , )
2009-06-10 07:24:35 -05:00
def execute ( self , aciname , * * kw ) :
"""
Execute the aci - show operation .
Returns the entry
: param uid : The login name of the user to retrieve .
: param kw : unused
"""
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 ' , [ ] ) )
2011-01-21 02:20:01 -06:00
aci = _find_aci_by_name ( acis , kw [ ' aciprefix ' ] , aciname )
2010-11-03 10:30:03 -05:00
if kw . get ( ' raw ' , False ) :
result = dict ( aci = unicode ( aci ) )
else :
result = _aci_to_kw ( ldap , aci )
2009-12-11 16:35:06 -06:00
return dict (
2010-11-03 10:30:03 -05:00
result = result ,
2009-12-11 16:35:06 -06:00
value = aciname ,
)
2009-06-10 07:24:35 -05:00
2009-06-16 07:38:27 -05:00
api . register ( aci_show )
2010-12-01 10:23:52 -06:00
class aci_rename ( crud . Update ) :
"""
Rename an ACI .
"""
2011-01-20 14:07:43 -06:00
NO_CLI = True
2010-12-01 10:23:52 -06:00
has_output_params = (
Str ( ' aci ' ,
label = _ ( ' ACI ' ) ,
) ,
)
takes_options = (
2011-01-21 02:20:01 -06:00
_prefix_option ,
2010-12-01 10:23:52 -06:00
Str ( ' newname ' ,
doc = _ ( ' New ACI name ' ) ,
) ,
)
2011-01-21 02:20:01 -06:00
msg_summary = _ ( ' Renamed ACI to " %(value)s " ' )
2010-12-01 10:23:52 -06:00
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 ' , [ ] ) )
2011-01-21 02:20:01 -06:00
aci = _find_aci_by_name ( acis , kw [ ' aciprefix ' ] , aciname )
2010-12-01 10:23:52 -06:00
for a in acis :
2011-01-21 02:20:01 -06:00
prefix , name = _parse_aci_name ( a . name )
if _make_aci_name ( prefix , kw [ ' newname ' ] ) == a . name :
2010-12-01 10:23:52 -06:00
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.
2011-01-20 15:35:34 -06:00
newaci = _make_aci ( ldap , None , kw [ ' newname ' ] , newkw )
2010-12-01 10:23:52 -06:00
2011-01-21 02:20:01 -06:00
self . api . Command [ ' aci_del ' ] ( aciname , * * kw )
2010-12-01 10:23:52 -06:00
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 )