freeipa/ipatests/test_xmlrpc/test_servicedelegation_plugin.py
Alexander Bokovoy 1f82d281cc service delegation: allow to add and remove host principals
Service delegation rules and targets deal with Kerberos principals.
As FreeIPA has separate service objects for hosts and Kerberos services,
it is not possible to specify host principal in the service delegation
rule or a target because the code assumes it always operates on Kerberos
service objects.

Simplify the code to add and remove members from delegation rules and
targets. New code looks up a name of the principal in cn=accounts,$BASEDN
as a krbPrincipalName attribute of an object with krbPrincipalAux object
class. This search path is optimized already for Kerberos KDC driver.

To support host principals, the specified principal name is checked to
have only one component (a host name). Service principals have more than
one component, typically service name and a host name, separated by '/'
sign. If the principal name has only one component, the name is
prepended with 'host/' to be able to find a host principal.

The logic described above allows to capture also aliases of both
Kerberos service and host principals. Additional check was added to
allow specifying single-component aliases ending with '$' sign. These
are typically used for Active Directory-related services like databases
or file services.

RN: service delegation rules and targets now allow to specify hosts as
RN: a rule or a target's member principal.

Fixes: https://pagure.io/freeipa/issue/8289
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
2020-05-14 21:47:17 +03:00

666 lines
20 KiB
Python

#
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
#
"""
Test the `ipaserver/plugins/serviceconstraint.py` module.
"""
from ipalib import api, errors
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import Declarative
from ipapython.dn import DN
import pytest
rule1 = u'test1'
rule2 = u'test rule two'
target1 = u'test1-targets'
target2 = u'test2-targets'
princ1 = u'HTTP/%s@%s' % (api.env.host, api.env.realm)
princ2 = u'ldap/%s@%s' % (api.env.host, api.env.realm)
princ3 = u'host/%s@%s' % (api.env.host, api.env.realm)
host3 = api.env.host
def get_servicedelegation_dn(cn):
return DN(('cn', cn), api.env.container_s4u2proxy, api.env.basedn)
@pytest.mark.tier1
class test_servicedelegation(Declarative):
cleanup_commands = [
('servicedelegationrule_del', [rule1], {}),
('servicedelegationrule_del', [rule2], {}),
('servicedelegationtarget_del', [target1], {}),
('servicedelegationtarget_del', [target2], {}),
]
tests = [
################
# create rule1:
dict(
desc='Try to retrieve non-existent %r' % rule1,
command=('servicedelegationrule_show', [rule1], {}),
expected=errors.NotFound(
reason=u'%s: service delegation rule not found' % rule1
),
),
dict(
desc='Try to delete non-existent %r' % rule1,
command=('servicedelegationrule_del', [rule1], {}),
expected=errors.NotFound(
reason=u'%s: service delegation rule not found' % rule1
),
),
dict(
desc='Create %r' % rule1,
command=(
'servicedelegationrule_add', [rule1], {}
),
expected=dict(
value=rule1,
summary=u'Added service delegation rule "%s"' % rule1,
result=dict(
cn=[rule1],
objectclass=objectclasses.servicedelegationrule,
dn=get_servicedelegation_dn(rule1),
),
),
),
dict(
desc='Try to create duplicate %r' % rule1,
command=(
'servicedelegationrule_add', [rule1], {}
),
expected=errors.DuplicateEntry(
message=u'service delegation rule with name "%s" '
'already exists' % rule1),
),
dict(
desc='Retrieve %r' % rule1,
command=('servicedelegationrule_show', [rule1], {}),
expected=dict(
value=rule1,
summary=None,
result=dict(
cn=[rule1],
dn=get_servicedelegation_dn(rule1),
),
),
),
dict(
desc='Search for %r' % rule1,
command=('servicedelegationrule_find', [], dict(cn=rule1)),
expected=dict(
count=1,
truncated=False,
result=[
dict(
dn=get_servicedelegation_dn(rule1),
cn=[rule1],
),
],
summary=u'1 service delegation rule matched',
),
),
################
# create rule2:
dict(
desc='Create %r' % rule2,
command=(
'servicedelegationrule_add', [rule2], {}
),
expected=dict(
value=rule2,
summary=u'Added service delegation rule "%s"' % rule2,
result=dict(
cn=[rule2],
objectclass=objectclasses.servicedelegationrule,
dn=get_servicedelegation_dn(rule2),
),
),
),
dict(
desc='Search for all rules with members',
command=('servicedelegationrule_find', [], {'no_members': False}),
expected=dict(
summary=u'3 service delegation rules matched',
count=3,
truncated=False,
result=[
{
'dn': get_servicedelegation_dn(u'ipa-http-delegation'),
'cn': [u'ipa-http-delegation'],
'memberprincipal': [princ1],
'ipaallowedtarget_servicedelegationtarget':
[u'ipa-ldap-delegation-targets',
u'ipa-cifs-delegation-targets']
},
dict(
dn=get_servicedelegation_dn(rule2),
cn=[rule2],
),
dict(
dn=get_servicedelegation_dn(rule1),
cn=[rule1],
),
],
),
),
dict(
desc='Search for all rules',
command=('servicedelegationrule_find', [], {}),
expected=dict(
summary=u'3 service delegation rules matched',
count=3,
truncated=False,
result=[
{
'dn': get_servicedelegation_dn(u'ipa-http-delegation'),
'cn': [u'ipa-http-delegation'],
'memberprincipal': [princ1],
},
dict(
dn=get_servicedelegation_dn(rule2),
cn=[rule2],
),
dict(
dn=get_servicedelegation_dn(rule1),
cn=[rule1],
),
],
),
),
dict(
desc='Create target %r' % target1,
command=(
'servicedelegationtarget_add', [target1], {}
),
expected=dict(
value=target1,
summary=u'Added service delegation target "%s"' % target1,
result=dict(
cn=[target1],
objectclass=objectclasses.servicedelegationtarget,
dn=get_servicedelegation_dn(target1),
),
),
),
dict(
desc='Create target %r' % target2,
command=(
'servicedelegationtarget_add', [target2], {}
),
expected=dict(
value=target2,
summary=u'Added service delegation target "%s"' % target2,
result=dict(
cn=[target2],
objectclass=objectclasses.servicedelegationtarget,
dn=get_servicedelegation_dn(target2),
),
),
),
dict(
desc='Search for all targets',
command=('servicedelegationtarget_find', [], {}),
expected=dict(
summary=u'4 service delegation targets matched',
count=4,
truncated=False,
result=[
{
'dn': get_servicedelegation_dn(
u'ipa-cifs-delegation-targets'),
'cn': [u'ipa-cifs-delegation-targets'],
},
{
'dn': get_servicedelegation_dn(
u'ipa-ldap-delegation-targets'
),
'cn': [u'ipa-ldap-delegation-targets'],
'memberprincipal': [princ2],
},
dict(
dn=get_servicedelegation_dn(target1),
cn=[target1],
),
dict(
dn=get_servicedelegation_dn(target2),
cn=[target2],
),
],
),
),
###############
# member stuff:
dict(
desc='Add member %r to %r' % (target1, rule1),
command=(
'servicedelegationrule_add_target', [rule1],
dict(servicedelegationtarget=target1)
),
expected=dict(
completed=1,
failed=dict(
ipaallowedtarget=dict(
servicedelegationtarget=tuple(),
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'ipaallowedtarget_servicedelegationtarget': (target1,),
'cn': [rule1],
},
),
),
dict(
desc='Add duplicate target %r to %r' % (target1, rule1),
command=(
'servicedelegationrule_add_target', [rule1],
dict(servicedelegationtarget=target1)
),
expected=dict(
completed=0,
failed=dict(
ipaallowedtarget=dict(
servicedelegationtarget=[
[target1, u'This entry is already a member']
],
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'ipaallowedtarget_servicedelegationtarget': (target1,),
'cn': [rule1],
},
),
),
dict(
desc='Add non-existent target %r to %r' % (u'notfound', rule1),
command=(
'servicedelegationrule_add_target', [rule1],
dict(servicedelegationtarget=u'notfound')
),
expected=dict(
completed=0,
failed=dict(
ipaallowedtarget=dict(
servicedelegationtarget=[
[u'notfound', u'no such entry']
],
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'ipaallowedtarget_servicedelegationtarget': (target1,),
'cn': [rule1],
},
),
),
dict(
desc='Remove a target %r from %r' % (target1, rule1),
command=(
'servicedelegationrule_remove_target', [rule1],
dict(servicedelegationtarget=target1)
),
expected=dict(
completed=1,
failed=dict(
ipaallowedtarget=dict(
servicedelegationtarget=tuple(),
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'cn': [rule1],
},
),
),
dict(
desc='Remove non-existent target %r from %r' % (
u'notfound', rule1
),
command=(
'servicedelegationrule_remove_target', [rule1],
dict(servicedelegationtarget=u'notfound')
),
expected=dict(
completed=0,
failed=dict(
ipaallowedtarget=dict(
servicedelegationtarget=[
[u'notfound', u'This entry is not a member']
],
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'cn': [rule1],
},
),
),
###############
# memberprincipal member stuff:
dict(
desc='Add memberprinc %r to %r' % (princ1, rule1),
command=(
'servicedelegationrule_add_member', [rule1],
dict(principal=princ1)
),
expected=dict(
completed=1,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=tuple(),
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'memberprincipal': (princ1,),
'cn': [rule1],
},
),
),
dict(
desc='Add duplicate member %r to %r' % (princ1, rule1),
command=(
'servicedelegationrule_add_member', [rule1],
dict(principal=princ1)
),
expected=dict(
completed=0,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=[
[princ1, u'This entry is already a member']
],
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'memberprincipal': (princ1,),
'cn': [rule1],
},
),
),
dict(
desc='Add non-existent member %r to %r' % (
u'HTTP/notfound', rule1
),
command=(
'servicedelegationrule_add_member', [rule1],
dict(principal=u'HTTP/notfound@%s' % api.env.realm)
),
expected=dict(
completed=0,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=[
[u'HTTP/notfound@%s' % api.env.realm,
u'no matching entry found']
],
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'memberprincipal': (princ1,),
'cn': [rule1],
},
),
),
dict(
desc='Add host as a member %r to %r' % (host3, rule1),
command=(
'servicedelegationrule_add_member', [rule1],
dict(principal=princ3)
),
expected=dict(
completed=1,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=tuple(),
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'memberprincipal': (princ1, princ3),
'cn': [rule1],
},
),
),
dict(
desc='Remove a host member %r from %r' % (host3, rule1),
command=(
'servicedelegationrule_remove_member', [rule1],
dict(principal=host3)
),
expected=dict(
completed=1,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=tuple(),
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'memberprincipal': (princ1,),
'cn': [rule1],
},
),
),
dict(
desc='Remove a member %r from %r' % (princ1, rule1),
command=(
'servicedelegationrule_remove_member', [rule1],
dict(principal=princ1)
),
expected=dict(
completed=1,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=tuple(),
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'memberprincipal': [],
'cn': [rule1],
},
),
),
dict(
desc='Remove non-existent member %r from %r' % (
u'HTTP/notfound', rule1
),
command=(
'servicedelegationrule_remove_member', [rule1],
dict(principal=u'HTTP/notfound@%s' % api.env.realm)
),
expected=dict(
completed=0,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=[
[u'HTTP/notfound@%s' % api.env.realm,
u'This entry is not a member']
],
),
),
result={
'dn': get_servicedelegation_dn(rule1),
'cn': [rule1],
},
),
),
dict(
desc='Add memberprinc %r to %r' % (princ1, target1),
command=(
'servicedelegationtarget_add_member', [target1],
dict(principal=princ1)
),
expected=dict(
completed=1,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=tuple(),
),
),
result={
'dn': get_servicedelegation_dn(target1),
'memberprincipal': (princ1,),
'cn': [target1],
},
),
),
dict(
desc='Add duplicate member %r to %r' % (princ1, target1),
command=(
'servicedelegationtarget_add_member', [target1],
dict(principal=princ1)
),
expected=dict(
completed=0,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=[
[princ1, u'This entry is already a member']
],
),
),
result={
'dn': get_servicedelegation_dn(target1),
'memberprincipal': (princ1,),
'cn': [target1],
},
),
),
dict(
desc='Add non-existent member %r to %r' % (
u'HTTP/notfound', target1
),
command=(
'servicedelegationtarget_add_member', [target1],
dict(principal=u'HTTP/notfound@%s' % api.env.realm)
),
expected=dict(
completed=0,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=[
[u'HTTP/notfound@%s' % api.env.realm,
u'no matching entry found']
],
),
),
result={
'dn': get_servicedelegation_dn(target1),
'memberprincipal': (princ1,),
'cn': [target1],
},
),
),
dict(
desc='Remove a member %r from %r' % (princ1, target1),
command=(
'servicedelegationtarget_remove_member', [target1],
dict(principal=princ1)
),
expected=dict(
completed=1,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=tuple(),
),
),
result={
'dn': get_servicedelegation_dn(target1),
'memberprincipal': [],
'cn': [target1],
},
),
),
dict(
desc='Remove non-existent member %r from %r' % (
u'HTTP/notfound', target1
),
command=(
'servicedelegationtarget_remove_member', [target1],
dict(principal=u'HTTP/notfound@%s' % api.env.realm)
),
expected=dict(
completed=0,
failed=dict(
failed_memberprincipal=dict(
memberprincipal=[
[u'HTTP/notfound@%s' % api.env.realm,
u'This entry is not a member']
],
),
),
result={
'dn': get_servicedelegation_dn(target1),
'cn': [target1],
},
),
),
]