Add support for sudoOrder

Update ipaSudoRule objectClass on upgrades to add new attributes.
Ensure uniqueness of sudoOrder in rules.

The attributes sudoNotBefore and sudoNotAfter are being added to
schema but not as Params.

https://fedorahosted.org/freeipa/ticket/1314
This commit is contained in:
Rob Crittenden 2012-03-01 14:02:28 -05:00
parent 31eebda584
commit d55d8bfa7e
7 changed files with 94 additions and 9 deletions

View File

@ -2831,7 +2831,7 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('value', <type 'unicode'>, None)
command: sudorule_add
args: 1,14,3
args: 1,15,3
arg: Str('cn', attribute=True, cli_name='sudorule_name', multivalue=False, primary_key=True, required=True)
option: Str('description', attribute=True, cli_name='desc', multivalue=False, required=False)
option: StrEnum('usercategory', attribute=True, cli_name='usercat', multivalue=False, required=False, values=(u'all',))
@ -2839,6 +2839,7 @@ option: StrEnum('hostcategory', attribute=True, cli_name='hostcat', multivalue=F
option: StrEnum('cmdcategory', attribute=True, cli_name='cmdcat', multivalue=False, required=False, values=(u'all',))
option: StrEnum('ipasudorunasusercategory', attribute=True, cli_name='runasusercat', multivalue=False, required=False, values=(u'all',))
option: StrEnum('ipasudorunasgroupcategory', attribute=True, cli_name='runasgroupcat', multivalue=False, required=False, values=(u'all',))
option: Int('sudoorder', attribute=True, cli_name='order', default=0, multivalue=False, required=False)
option: Str('externaluser', attribute=True, cli_name='externaluser', multivalue=False, required=False)
option: Str('ipasudorunasextuser', attribute=True, cli_name='runasexternaluser', multivalue=False, required=False)
option: Str('ipasudorunasextgroup', attribute=True, cli_name='runasexternalgroup', multivalue=False, required=False)
@ -2936,7 +2937,7 @@ args: 1,0,1
arg: Str('cn', attribute=True, cli_name='sudorule_name', multivalue=False, primary_key=True, query=True, required=True)
output: Output('result', None, None)
command: sudorule_find
args: 1,16,4
args: 1,17,4
arg: Str('criteria?', noextrawhitespace=False)
option: Str('cn', attribute=True, autofill=False, cli_name='sudorule_name', multivalue=False, primary_key=True, query=True, required=False)
option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False)
@ -2945,6 +2946,7 @@ option: StrEnum('hostcategory', attribute=True, autofill=False, cli_name='hostca
option: StrEnum('cmdcategory', attribute=True, autofill=False, cli_name='cmdcat', multivalue=False, query=True, required=False, values=(u'all',))
option: StrEnum('ipasudorunasusercategory', attribute=True, autofill=False, cli_name='runasusercat', multivalue=False, query=True, required=False, values=(u'all',))
option: StrEnum('ipasudorunasgroupcategory', attribute=True, autofill=False, cli_name='runasgroupcat', multivalue=False, query=True, required=False, values=(u'all',))
option: Int('sudoorder', attribute=True, autofill=False, cli_name='order', default=0, multivalue=False, query=True, required=False)
option: Str('externaluser', attribute=True, autofill=False, cli_name='externaluser', multivalue=False, query=True, required=False)
option: Str('ipasudorunasextuser', attribute=True, autofill=False, cli_name='runasexternaluser', multivalue=False, query=True, required=False)
option: Str('ipasudorunasextgroup', attribute=True, autofill=False, cli_name='runasexternalgroup', multivalue=False, query=True, required=False)
@ -2959,7 +2961,7 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
output: Output('count', <type 'int'>, None)
output: Output('truncated', <type 'bool'>, None)
command: sudorule_mod
args: 1,16,3
args: 1,17,3
arg: Str('cn', attribute=True, cli_name='sudorule_name', multivalue=False, primary_key=True, query=True, required=True)
option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, required=False)
option: StrEnum('usercategory', attribute=True, autofill=False, cli_name='usercat', multivalue=False, required=False, values=(u'all',))
@ -2967,6 +2969,7 @@ option: StrEnum('hostcategory', attribute=True, autofill=False, cli_name='hostca
option: StrEnum('cmdcategory', attribute=True, autofill=False, cli_name='cmdcat', multivalue=False, required=False, values=(u'all',))
option: StrEnum('ipasudorunasusercategory', attribute=True, autofill=False, cli_name='runasusercat', multivalue=False, required=False, values=(u'all',))
option: StrEnum('ipasudorunasgroupcategory', attribute=True, autofill=False, cli_name='runasgroupcat', multivalue=False, required=False, values=(u'all',))
option: Int('sudoorder', attribute=True, autofill=False, cli_name='order', default=0, multivalue=False, required=False)
option: Str('externaluser', attribute=True, autofill=False, cli_name='externaluser', multivalue=False, required=False)
option: Str('ipasudorunasextuser', attribute=True, autofill=False, cli_name='runasexternaluser', multivalue=False, required=False)
option: Str('ipasudorunasextgroup', attribute=True, autofill=False, cli_name='runasexternalgroup', multivalue=False, required=False)

View File

@ -32,7 +32,7 @@ attributeTypes: (2.16.840.1.113730.3.8.7.12 NAME 'hostMask' DESC 'IP mask to ide
## Attribute to store sudo command
attributeTypes: (2.16.840.1.113730.3.8.7.13 NAME 'sudoCmd' DESC 'Command(s) to be executed by sudo' EQUALITY caseExactMatch ORDERING caseExactMatch SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2' )
## Object class for SUDO rules
objectClasses: (2.16.840.1.113730.3.8.8.1 NAME 'ipaSudoRule' SUP ipaAssociation STRUCTURAL MAY ( externalUser $ externalHost $ hostMask $ memberAllowCmd $ memberDenyCmd $ cmdCategory $ ipaSudoOpt $ ipaSudoRunAs $ ipaSudoRunAsExtUser $ ipaSudoRunAsUserCategory $ ipaSudoRunAsGroup $ ipaSudoRunAsExtGroup $ ipaSudoRunAsGroupCategory ) X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.8.1 NAME 'ipaSudoRule' SUP ipaAssociation STRUCTURAL MAY ( externalUser $ externalHost $ hostMask $ memberAllowCmd $ memberDenyCmd $ cmdCategory $ ipaSudoOpt $ ipaSudoRunAs $ ipaSudoRunAsExtUser $ ipaSudoRunAsUserCategory $ ipaSudoRunAsGroup $ ipaSudoRunAsExtGroup $ ipaSudoRunAsGroupCategory $ sudoNotBefore $ sudoNotAfter $$ sudoOrder ) X-ORIGIN 'IPA v2' )
## Object class for SUDO commands
objectClasses: (2.16.840.1.113730.3.8.8.2 NAME 'ipaSudoCmd' DESC 'IPA object class for SUDO command' STRUCTURAL MUST ( ipaUniqueID $ sudoCmd ) MAY ( memberOf $ description ) X-ORIGIN 'IPA v2' )
## Object class for groups of the SUDO commands

View File

@ -9,7 +9,7 @@ app_DATA = \
60basev2.ldif \
60basev3.ldif \
60ipadns.ldif \
60ipasudo.ldif \
65ipasudo.ldif \
anonymous-vlv.ldif \
bootstrap-template.ldif \
caJarSigningCert.cfg.template \

View File

@ -38,3 +38,5 @@ add:attributeTypes: ( 1.3.6.1.4.1.15953.9.1.10
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
X-ORIGIN 'SUDO' )
replace:objectClasses:( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' DESC 'Sudoer Entries' STRUCTURAL MUST cn MAY ( sudoUser $$ sudoHost $$ sudoCommand $$ sudoRunAs $$ sudoOption $$ description ) X-ORIGIN 'SUDO' )::( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL DESC 'Sudoer Entries' MUST ( cn ) MAY ( sudoUser $$ sudoHost $$ sudoCommand $$ sudoRunAs $$ sudoRunAsUser $$ sudoRunAsGroup $$ sudoOption $$ sudoNotBefore $$ sudoNotAfter $$ sudoOrder $$ description ) X-ORIGIN 'SUDO')
replace:objectClasses: ( 2.16.840.1.113730.3.8.8.1 NAME 'ipaSudoRule' SUP ipaAssociation STRUCTURAL MAY ( externalUser $$ externalHost $$ hostMask $$ memberAllowCmd $$ memberDenyCmd $$ cmdCategory $$ ipaSudoOpt $$ ipaSudoRunAs $$ ipaSudoRunAsExtUser $$ ipaSudoRunAsUserCategory $$ ipaSudoRunAsGroup $$ ipaSudoRunAsExtGroup $$ ipaSudoRunAsGroupCategory ) X-ORIGIN 'IPA v2' )::(2.16.840.1.113730.3.8.8.1 NAME 'ipaSudoRule' SUP ipaAssociation STRUCTURAL MAY ( externalUser $$ externalHost $$ hostMask $$ memberAllowCmd $$ memberDenyCmd $$ cmdCategory $$ ipaSudoOpt $$ ipaSudoRunAs $$ ipaSudoRunAsExtUser $$ ipaSudoRunAsUserCategory $$ ipaSudoRunAsGroup $$ ipaSudoRunAsExtGroup $$ ipaSudoRunAsGroupCategory $$ sudoNotBefore $$ sudoNotAfter $$ sudoOrder) X-ORIGIN 'IPA v2' )

View File

@ -40,6 +40,10 @@ FreeIPA provides a means to configure the various aspects of Sudo:
RunAsGroup: The group(s) whose gid rights Sudo will be invoked with.
Options: The various Sudoers Options that can modify Sudo's behavior.
An order can be added to a sudorule to control the order in which they
are evaluated (if the client supports it). This order is an integer and
must be unique.
FreeIPA provides a designated binddn to use with Sudo located at:
uid=sudo,cn=sysaccounts,cn=etc,dc=example,dc=com
@ -80,6 +84,7 @@ class sudorule(LDAPObject):
'memberallowcmd', 'memberdenycmd', 'ipasudoopt',
'ipasudorunas', 'ipasudorunasgroup',
'ipasudorunasusercategory', 'ipasudorunasgroupcategory',
'sudoorder',
]
uuid_attribute = 'ipauniqueid'
rdn_attribute = 'ipauniqueid'
@ -139,6 +144,13 @@ class sudorule(LDAPObject):
doc=_('RunAs Group category the rule applies to'),
values=(u'all', ),
),
Int('sudoorder?',
cli_name='order',
label=_('Sudo order'),
doc=_('integer to order the Sudo rules'),
default=0,
minvalue=0,
),
Str('memberuser_user?',
label=_('Users'),
flags=['no_create', 'no_update', 'no_search'],
@ -207,6 +219,25 @@ class sudorule(LDAPObject):
),
)
order_not_unique_msg = _(
'order must be a unique value (%(order)d already used by %(rule)s)'
)
def check_order_uniqueness(self, *keys, **options):
if 'sudoorder' in options:
entries = self.methods.find(
sudoorder=options['sudoorder']
)['result']
if len(entries) > 0:
rule_name = entries[0]['cn'][0]
raise errors.ValidationError(
name='order',
error=self.order_not_unique_msg % {
'order': options['sudoorder'],
'rule': rule_name,
}
)
api.register(sudorule)
@ -214,6 +245,7 @@ class sudorule_add(LDAPCreate):
__doc__ = _('Create new Sudo Rule.')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
self.obj.check_order_uniqueness(*keys, **options)
# Sudo Rules are enabled by default
entry_attrs['ipaenabledflag'] = 'TRUE'
return dn
@ -236,6 +268,15 @@ class sudorule_mod(LDAPUpdate):
msg_summary = _('Modified Sudo Rule "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
if 'sudoorder' in options:
new_order = options.get('sudoorder')
old_entry = self.api.Command.sudorule_show(keys[-1])['result']
if 'sudoorder' in old_entry:
old_order = int(old_entry['sudoorder'][0])
if old_order != new_order:
self.obj.check_order_uniqueness(*keys, **options)
else:
self.obj.check_order_uniqueness(*keys, **options)
try:
(_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes)
except errors.NotFound:

View File

@ -359,7 +359,7 @@ class DsInstance(service.Service):
"60basev2.ldif",
"60basev3.ldif",
"60ipadns.ldif",
"60ipasudo.ldif"):
"65ipasudo.ldif"):
target_fname = schema_dirname(self.serverid) + schema_fname
shutil.copyfile(ipautil.SHARE_DIR + schema_fname, target_fname)
os.chmod(target_fname, 0440) # read access for dirsrv user/group

View File

@ -30,6 +30,7 @@ class test_sudorule(XMLRPC_test):
Test the `sudorule` plugin.
"""
rule_name = u'testing_sudorule1'
rule_name2 = u'testing_sudorule2'
rule_command = u'/usr/bin/testsudocmd1'
rule_desc = u'description'
rule_desc_mod = u'description modified'
@ -38,8 +39,8 @@ class test_sudorule(XMLRPC_test):
test_external_user = u'external_test_user'
test_group = u'sudorule_test_group'
test_external_group = u'external_test_group'
test_host = u'sudorule.test-host'
test_external_host = u'external.test-host'
test_host = u'sudorule.testhost'
test_external_host = u'external.testhost'
test_hostgroup = u'sudorule_test_hostgroup'
test_sudoallowcmdgroup = u'sudorule_test_allowcmdgroup'
test_sudodenycmdgroup = u'sudorule_test_denycmdgroup'
@ -625,8 +626,45 @@ class test_sudorule(XMLRPC_test):
api.Command['sudocmdgroup_del'](self.test_sudoallowcmdgroup)
api.Command['sudocmdgroup_del'](self.test_sudodenycmdgroup)
def test_l_sudorule_order(self):
"""
Test that order uniqueness is maintained
"""
api.Command['sudorule_mod'](self.rule_name, sudoorder=1)
def test_l_sudorule_del(self):
api.Command['sudorule_add'](self.rule_name2)
# mod of rule that has no order and set a duplicate
try:
api.Command['sudorule_mod'](self.rule_name2, sudoorder=1)
except errors.ValidationError:
pass
# Remove the rule so we can re-add it
api.Command['sudorule_del'](self.rule_name2)
# add a new rule with a duplicate order
try:
api.Command['sudorule_add'](self.rule_name2, sudoorder=1)
except errors.ValidationError:
pass
# add a new rule with a unique order
api.Command['sudorule_add'](self.rule_name2, sudoorder=2)
try:
api.Command['sudorule_mod'](self.rule_name2, sudoorder=1)
except errors.ValidationError:
pass
# Try setting both to 0
api.Command['sudorule_mod'](self.rule_name2, sudoorder=0)
try:
api.Command['sudorule_mod'](self.rule_name, sudoorder=0)
except errors.ValidationError:
pass
def test_m_sudorule_del(self):
"""
Test deleting a Sudo rule using `xmlrpc.sudorule_del`.
"""
@ -638,3 +676,4 @@ class test_sudorule(XMLRPC_test):
pass
else:
assert False
api.Command['sudorule_del'](self.rule_name2)