freeipa/ipatests/test_xmlrpc/test_group_plugin.py

740 lines
28 KiB
Python
Raw Normal View History

2009-06-24 08:12:27 -05:00
# Authors:
# Rob Crittenden <rcritten@redhat.com>
# Pavel Zuna <pzuna@redhat.com>
# Filip Skola <fskola@redhat.com>
2009-06-24 08:12:27 -05:00
#
# Copyright (C) 2008 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, either version 3 of the License, or
# (at your option) any later version.
2009-06-24 08:12:27 -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
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2009-06-24 08:12:27 -05:00
"""
Test the `ipaserver/plugins/group.py` module.
2009-06-24 08:12:27 -05:00
"""
import pytest
from ipalib import errors
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import (
fuzzy_digits, fuzzy_uuid, fuzzy_set_ci, add_oc,
XMLRPC_test, raises_exact
)
from ipatests.test_xmlrpc.tracker.group_plugin import GroupTracker
from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker
from ipatests.util import assert_deepequal, get_group_dn
notagroup = u'notagroup'
renamedgroup1 = u'renamedgroup'
invalidgroup1 = u'+tgroup1'
external_sid1 = u'S-1-1-123456-789-1'
@pytest.fixture(scope='class')
def group(request):
tracker = GroupTracker(name=u'testgroup1', description=u'Test desc1')
return tracker.make_fixture(request)
@pytest.fixture(scope='class')
def group2(request):
tracker = GroupTracker(name=u'testgroup2', description=u'Test desc2')
return tracker.make_fixture(request)
@pytest.fixture(scope='class')
def managed_group(request, user):
user.ensure_exists()
tracker = GroupTracker(
name=user.uid, description=u'User private group for %s' % user.uid
)
tracker.exists = True
# Managed group gets created when user is created
tracker.track_create()
return tracker
@pytest.fixture(scope='class')
def user(request):
tracker = UserTracker(name=u'user1', givenname=u'Test', sn=u'User1')
return tracker.make_fixture(request)
@pytest.fixture(scope='class')
def user_npg2(request, group):
""" User tracker fixture for testing users with no private group """
tracker = UserTracker(name=u'npguser2', givenname=u'Npguser',
sn=u'Npguser2', noprivate=True, gidnumber=1000)
tracker.track_create()
del tracker.attrs['mepmanagedentry']
tracker.attrs.update(
gidnumber=[u'1000'], description=[], memberof_group=[group.cn],
objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs')
)
return tracker.make_fixture(request)
@pytest.fixture(scope='class')
def admins(request):
# Track the admins group
tracker = GroupTracker(
name=u'admins', description=u'Account administrators group'
)
tracker.exists = True
tracker.track_create()
tracker.attrs.update(member_user=[u'admin'])
return tracker
@pytest.fixture(scope='class')
def trustadmins(request):
# Track the 'trust admins' group
tracker = GroupTracker(
name=u'trust admins', description=u'Trusts administrators group'
)
tracker.exists = True
tracker.track_create()
tracker.attrs.update(member_user=[u'admin'])
return tracker
2009-06-24 08:12:27 -05:00
@pytest.mark.tier1
class TestGroup(XMLRPC_test):
def test_create(self, group):
""" Create a group """
group.create()
def test_create_duplicate(self, group):
""" Try to create a duplicate group """
group.ensure_exists()
command = group.make_create_command()
with raises_exact(errors.DuplicateEntry(
message=u'group with name "%s" already exists' % group.cn)):
command()
def test_retrieve(self, group):
""" Retrieve a group """
group.retrieve()
def test_update(self, group):
""" Update a group with new description
and perform retrieve command to verify the update """
group.update(dict(description=u'New desc'))
group.retrieve()
def test_rename(self, group):
""" Rename a group and than rename it back """
origname = group.cn
command = group.make_command('group_mod', *[group.cn],
**dict(setattr=u'cn=%s' % renamedgroup1))
result = command()
group.attrs.update(cn=[renamedgroup1])
group.check_update(result)
group.cn = renamedgroup1
command = group.make_command('group_mod', *[group.cn],
**dict(setattr=u'cn=%s' % origname))
result = command()
group.attrs.update(cn=[origname])
group.check_update(result)
group.cn = origname
def test_convert_posix_to_external(self, group):
""" Try to convert a posix group to external """
command = group.make_update_command(dict(external=True))
with raises_exact(errors.PosixGroupViolation(
reason=u"""This is already a posix group and cannot
be converted to external one""")):
command()
def test_add_with_invalid_name(self, group):
""" Try to add group with an invalid name """
command = group.make_command(
'group_add', *[invalidgroup1], **dict(description=u'Test')
)
with raises_exact(errors.ValidationError(
name='group_name',
error=u'may only include letters, numbers, _, -, . and $')):
command()
@pytest.mark.tier1
class TestFindGroup(XMLRPC_test):
def test_search(self, group):
""" Search for a group """
group.ensure_exists()
group.find()
def test_search_for_all_groups_with_members(self, group, group2):
""" Search for all groups """
group.ensure_exists()
group2.create()
command = group.make_command('group_find', no_members=False)
result = command()
assert_deepequal(dict(
summary=u'6 groups matched',
count=6,
truncated=False,
result=[
{
'dn': get_group_dn('admins'),
'member_user': [u'admin'],
'gidnumber': [fuzzy_digits],
'cn': [u'admins'],
'description': [u'Account administrators group'],
},
{
'dn': get_group_dn('editors'),
'gidnumber': [fuzzy_digits],
'cn': [u'editors'],
'description':
[u'Limited admins who can edit other users'],
},
{
'dn': get_group_dn('ipausers'),
'cn': [u'ipausers'],
'description': [u'Default group for all users'],
},
{
'dn': get_group_dn(group.cn),
'cn': [group.cn],
'description': [u'Test desc1'],
'gidnumber': [fuzzy_digits],
},
{
'dn': get_group_dn(group2.cn),
'cn': [group2.cn],
'description': [u'Test desc2'],
'gidnumber': [fuzzy_digits],
},
{
'dn': get_group_dn('trust admins'),
'member_user': [u'admin'],
'cn': [u'trust admins'],
'description': [u'Trusts administrators group'],
},
]), result)
def test_search_for_all_groups(self, group, group2):
""" Search for all groups """
group.ensure_exists()
group2.ensure_exists()
command = group.make_command('group_find')
result = command()
assert_deepequal(dict(
summary=u'6 groups matched',
count=6,
truncated=False,
result=[
{
'dn': get_group_dn('admins'),
'gidnumber': [fuzzy_digits],
'cn': [u'admins'],
'description': [u'Account administrators group'],
},
{
'dn': get_group_dn('editors'),
'gidnumber': [fuzzy_digits],
'cn': [u'editors'],
'description':
[u'Limited admins who can edit other users'],
},
{
'dn': get_group_dn('ipausers'),
'cn': [u'ipausers'],
'description': [u'Default group for all users'],
},
{
'dn': get_group_dn(group.cn),
'cn': [group.cn],
'description': [u'Test desc1'],
'gidnumber': [fuzzy_digits],
},
{
'dn': get_group_dn(group2.cn),
'cn': [group2.cn],
'description': [u'Test desc2'],
'gidnumber': [fuzzy_digits],
},
{
'dn': get_group_dn('trust admins'),
'cn': [u'trust admins'],
'description': [u'Trusts administrators group'],
},
]), result)
def test_search_for_all_posix(self, group, group2):
""" Search for all posix groups """
command = group.make_command(
'group_find', **dict(posix=True, all=True)
)
result = command()
assert_deepequal(dict(
summary=u'4 groups matched',
count=4,
truncated=False,
result=[
{
'dn': get_group_dn('admins'),
'member_user': [u'admin'],
'gidnumber': [fuzzy_digits],
'cn': [u'admins'],
'description': [u'Account administrators group'],
'objectclass': fuzzy_set_ci(add_oc(
objectclasses.posixgroup, u'ipantgroupattrs')),
'ipauniqueid': [fuzzy_uuid],
},
{
'dn': get_group_dn('editors'),
'gidnumber': [fuzzy_digits],
'cn': [u'editors'],
'description':
[u'Limited admins who can edit other users'],
'objectclass': fuzzy_set_ci(add_oc(
objectclasses.posixgroup, u'ipantgroupattrs')),
'ipauniqueid': [fuzzy_uuid],
},
{
'dn': get_group_dn(group.cn),
'cn': [group.cn],
'description': [u'Test desc1'],
'gidnumber': [fuzzy_digits],
'objectclass': fuzzy_set_ci(add_oc(
objectclasses.posixgroup, u'ipantgroupattrs')),
'ipauniqueid': [fuzzy_uuid],
},
{
'dn': get_group_dn(group2.cn),
'cn': [group2.cn],
'description': [u'Test desc2'],
'gidnumber': [fuzzy_digits],
'objectclass': fuzzy_set_ci(add_oc(
objectclasses.posixgroup, u'ipantgroupattrs')),
'ipauniqueid': [fuzzy_uuid],
},
]), result)
@pytest.mark.tier1
class TestNonexistentGroup(XMLRPC_test):
def test_retrieve_nonexistent(self, group):
""" Try to retrieve a non-existent group """
group.ensure_missing()
command = group.make_retrieve_command()
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % group.cn)):
command()
def test_update_nonexistent(self, group):
""" Try to update a non-existent group """
group.ensure_missing()
command = group.make_update_command(
updates=dict(description=u'hey'))
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % group.cn)):
command()
def test_delete_nonexistent(self, group):
""" Try to delete a non-existent user """
group.ensure_missing()
command = group.make_delete_command()
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % group.cn)):
command()
def test_rename_nonexistent(self, group):
""" Try to rename a non-existent user """
group.ensure_missing()
command = group.make_update_command(
updates=dict(setattr=u'cn=%s' % renamedgroup1))
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % group.cn)):
command()
@pytest.mark.tier1
class TestNonposixGroup(XMLRPC_test):
def test_create_nonposix(self, group):
""" Create a non-posix group """
group.track_create()
command = group.make_create_command(**dict(nonposix=True))
result = command()
del group.attrs['gidnumber']
group.attrs.update(objectclass=objectclasses.group)
group.check_create(result)
def test_create_duplicate_to_nonposix(self, group):
""" Try to create a duplicate non-posix group """
group.ensure_exists()
command = group.make_create_command()
with raises_exact(errors.DuplicateEntry(
message=u'group with name "%s" already exists' % group.cn)):
command()
def test_retrieve_nonposix(self, group):
""" Retrieve a non-posix group """
group.retrieve()
def test_update_nonposix(self, group):
""" Update a non-posix group with new description
and perform retrieve command to verify the update """
group.update(dict(description=u'New desc'))
group.retrieve()
def test_search_for_all_nonposix(self, group):
""" Perform a search for all non-posix groups """
command = group.make_command(
'group_find', **dict(nonposix=True, all=True)
)
result = command()
assert_deepequal(dict(
summary=u'3 groups matched',
count=3,
truncated=False,
result=[
{
'dn': get_group_dn('ipausers'),
'cn': [u'ipausers'],
'description': [u'Default group for all users'],
'objectclass': fuzzy_set_ci(objectclasses.group),
'ipauniqueid': [fuzzy_uuid],
},
{
'dn': get_group_dn(group.cn),
'cn': [group.cn],
'description': [u'New desc'],
'objectclass': fuzzy_set_ci(objectclasses.group),
'ipauniqueid': [fuzzy_uuid],
},
{
'dn': get_group_dn('trust admins'),
'member_user': [u'admin'],
'cn': [u'trust admins'],
'description': [u'Trusts administrators group'],
'objectclass': fuzzy_set_ci(objectclasses.group),
'ipauniqueid': [fuzzy_uuid],
},
],
), result)
def test_upgrade_nonposix_to_posix(self, group):
""" Update non-posix group to promote it to posix group """
group.attrs.update(gidnumber=[fuzzy_digits])
group.update(dict(posix=True), dict(posix=None))
group.retrieve()
def test_search_for_all_nonposix_with_criteria(self, group):
""" Search for all non-posix groups with additional
criteria filter """
command = group.make_command(
'group_find', *[u'users'], **dict(nonposix=True, all=True)
)
result = command()
assert_deepequal(dict(
summary=u'1 group matched',
count=1,
truncated=False,
result=[
{
'dn': get_group_dn('ipausers'),
'cn': [u'ipausers'],
'description': [u'Default group for all users'],
'objectclass': fuzzy_set_ci(objectclasses.group),
'ipauniqueid': [fuzzy_uuid],
},
],
), result)
@pytest.mark.tier1
class TestExternalGroup(XMLRPC_test):
def test_create_external(self, group):
""" Create a non-posix group """
group.track_create()
del group.attrs['gidnumber']
group.attrs.update(objectclass=objectclasses.externalgroup)
command = group.make_create_command(**dict(external=True))
result = command()
group.check_create(result)
def test_search_for_external(self, group):
""" Search for all external groups """
command = group.make_command(
'group_find', **dict(external=True, all=True)
)
result = command()
group.check_find(result, all=True)
def test_convert_external_to_posix(self, group):
""" Try to convert an external group to posix """
command = group.make_update_command(dict(posix=True))
with raises_exact(errors.ExternalGroupViolation(
reason=u'This group cannot be posix because it is external')):
command()
def test_add_external_member_to_external(self, group):
""" Try to add an invalid external member to an external
group and check that proper exceptions are raised """
# When adding external SID member to a group we can't test
# it fully due to possibly missing Samba 4 python bindings
# and/or not configured AD trusts. Thus, we'll use incorrect
# SID value to merely test that proper exceptions are raised
command = group.make_command('group_add_member', *[group.cn],
**dict(ipaexternalmember=external_sid1))
try:
command()
except Exception as ex:
if type(ex) == errors.ValidationError:
pass
elif type(ex) == errors.NotFound:
pass
elif 'failed' in str(ex):
pass
else:
raise ex
def test_delete_external_group(self, group):
group.delete()
@pytest.mark.tier1
class TestGroupMember(XMLRPC_test):
def test_add_nonexistent_member(self, group):
""" Try to add non-existent member to a group """
group.create()
command = group.make_add_member_command(dict(group=notagroup))
result = command()
group.check_add_member_negative(result, dict(group=notagroup))
def test_remove_nonexistent_member(self, group):
""" Try to remove non-existent member from a group """
group.ensure_exists()
command = group.make_remove_member_command(dict(group=notagroup))
result = command()
group.check_remove_member_negative(result, dict(group=notagroup))
def test_add_member(self, group, group2):
""" Add member group to a group """
group.ensure_exists()
group2.ensure_exists()
group.add_member(dict(group=group2.cn))
def test_remove_member(self, group, group2):
""" Remove a group member """
group.ensure_exists()
group2.ensure_exists()
group.remove_member(dict(group=group2.cn))
def test_add_and_remove_group_from_admins(self, group, admins):
""" Add group to protected admins group and then remove it """
# Test scenario from ticket #4448
# https://fedorahosted.org/freeipa/ticket/4448
group.ensure_exists()
admins.add_member(dict(group=group.cn))
admins.remove_member(dict(group=group.cn))
@pytest.mark.tier1
class TestValidation(XMLRPC_test):
# The assumption for this class of tests is that if we don't
# get a validation error then the request was processed normally.
def test_validation_disabled_on_delete(self, group):
""" Test that validation is disabled on group deletes """
command = group.make_command('group_del', invalidgroup1)
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % invalidgroup1)):
command()
def test_validation_disabled_on_show(self, group):
""" Test that validation is disabled on group retrieves """
command = group.make_command('group_show', invalidgroup1)
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % invalidgroup1)):
command()
def test_validation_disabled_on_mod(self, group):
""" Test that validation is disabled on group mods """
command = group.make_command('group_mod', invalidgroup1)
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % invalidgroup1)):
command()
@pytest.mark.tier1
class TestManagedGroups(XMLRPC_test):
def test_verify_managed_created(self, managed_group):
""" Verify that managed group is created with new user """
managed_group.retrieve()
def test_verify_managed_findable(self, managed_group):
""" Verify that managed group can be found """
command = managed_group.make_find_command(
**dict(cn=managed_group.cn, private=True)
)
result = command()
managed_group.check_find(result)
def test_delete_managed(self, managed_group):
""" Try to delete managed group """
command = managed_group.make_delete_command()
with raises_exact(errors.ManagedGroupError()):
command()
def test_detach_managed(self, managed_group):
""" Detach managed group from a user """
command = managed_group.make_detach_command()
result = command()
managed_group.check_detach(result)
def test_delete_detached_managed(self, managed_group, user):
""" Delete a previously managed group that is now detached
and verify it's really gone """
managed_group.delete()
command = managed_group.make_retrieve_command()
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % managed_group.cn)):
command()
user.ensure_missing()
def test_verify_managed_missing_for_user_without_upg(self, user_npg2):
""" Create a user without user private group and
verify private group wasn't created """
user_npg2.attrs.update(memberof_group=[u'ipausers'])
command = user_npg2.make_create_command()
result = command()
user_npg2.check_create(result, [u'description', u'memberof_group'])
command = user_npg2.make_command('group_show', *[user_npg2.uid])
with raises_exact(errors.NotFound(
reason=u'%s: group not found' % user_npg2.uid)):
command()
@pytest.mark.tier1
class TestManagedGroupObjectclasses(XMLRPC_test):
def test_check_objectclasses_after_detach(self, user, managed_group):
""" Check objectclasses after user was detached from managed group """
# https://fedorahosted.org/freeipa/ticket/4909#comment:1
user.ensure_exists()
user.run_command('group_detach', *[user.uid])
managed_group.retrieve(all=True)
managed_group.add_member(dict(user=user.uid))
managed_group.ensure_missing()
user.ensure_missing()
@pytest.mark.tier1
class TestAdminGroup(XMLRPC_test):
def test_remove_admin_from_admins(self, admins):
""" Remove the original admin from admins group """
command = admins.make_remove_member_command(
dict(user=u'admin')
)
with raises_exact(errors.LastMemberError(
key=u'admin', label=u'group', container=admins.cn)):
command()
def test_add_another_admin(self, admins, user):
""" Add second member to the admins group """
user.ensure_exists()
admins.add_member(dict(user=user.uid))
def test_remove_all_admins_from_admins(self, admins, user):
""" Try to remove both original and our admin from admins group """
command = admins.make_remove_member_command(
dict(user=[u'admin', user.uid])
)
with raises_exact(errors.LastMemberError(
key=u'admin', label=u'group', container=admins.cn)):
command()
def test_delete_admins(self, admins):
""" Try to delete the protected admins group """
command = admins.make_delete_command()
with raises_exact(errors.ProtectedEntryError(label=u'group',
key=admins.cn, reason='privileged group')):
command()
def test_rename_admins(self, admins):
""" Try to rename the protected admins group """
command = admins.make_command('group_mod', *[admins.cn],
**dict(rename=renamedgroup1))
with raises_exact(errors.ProtectedEntryError(label=u'group',
key=admins.cn, reason='Cannot be renamed')):
command()
def test_rename_admins_using_setattr(self, admins):
""" Try to rename the protected admins group using setattr """
command = admins.make_command('group_mod', *[admins.cn],
**dict(setattr=u'cn=%s' % renamedgroup1))
with raises_exact(errors.ProtectedEntryError(label=u'group',
key=admins.cn, reason='Cannot be renamed')):
command()
def test_update_admins_to_support_external_membership(self, admins):
""" Try to modify the admins group to support external membership """
command = admins.make_command('group_mod', *[admins.cn],
**dict(external=True))
with raises_exact(errors.ProtectedEntryError(label=u'group',
key=admins.cn,
reason='Cannot support external non-IPA members')):
command()
@pytest.mark.tier1
class TestTrustAdminGroup(XMLRPC_test):
def test_delete_trust_admins(self, trustadmins):
""" Try to delete the protected 'trust admins' group """
command = trustadmins.make_delete_command()
with raises_exact(errors.ProtectedEntryError(label=u'group',
key=trustadmins.cn, reason='privileged group')):
command()
def test_rename_trust_admins(self, trustadmins):
""" Try to rename the protected 'trust admins' group """
command = trustadmins.make_command('group_mod', *[trustadmins.cn],
**dict(rename=renamedgroup1))
with raises_exact(errors.ProtectedEntryError(label=u'group',
key=trustadmins.cn, reason='Cannot be renamed')):
command()
def test_rename_trust_admins_using_setattr(self, trustadmins):
""" Try to rename the protected 'trust admins' group using setattr """
command = trustadmins.make_command(
'group_mod', *[trustadmins.cn],
**dict(setattr=u'cn=%s' % renamedgroup1)
)
with raises_exact(errors.ProtectedEntryError(label=u'group',
key=trustadmins.cn, reason='Cannot be renamed')):
command()
def test_update_trust_admins_to_support_external_membership(
self, trustadmins
):
""" Try to modify the 'trust admins' group to
support external membership """
command = trustadmins.make_command(
'group_mod', *[trustadmins.cn],
**dict(external=True)
)
with raises_exact(errors.ProtectedEntryError(label=u'group',
key=trustadmins.cn,
reason='Cannot support external non-IPA members')):
command()