diff options
Diffstat (limited to 'ipatests/test_xmlrpc')
-rw-r--r-- | ipatests/test_xmlrpc/test_user_plugin.py | 2377 | ||||
-rw-r--r-- | ipatests/test_xmlrpc/tracker/user_plugin.py | 169 |
2 files changed, 1026 insertions, 1520 deletions
diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py index 084fb83c4..355573138 100644 --- a/ipatests/test_xmlrpc/test_user_plugin.py +++ b/ipatests/test_xmlrpc/test_user_plugin.py @@ -2,6 +2,7 @@ # Rob Crittenden <rcritten@redhat.com> # Pavel Zuna <pzuna@redhat.com> # Jason Gerard DeRose <jderose@redhat.com> +# Filip Skola <fskola@redhat.com> # # Copyright (C) 2008, 2009 Red Hat # see file 'COPYING' for use and warranty information @@ -23,6 +24,7 @@ Test the `ipalib/plugins/user.py` module. """ +import pytest import datetime import ldap import re @@ -30,42 +32,41 @@ import re from ipalib import api, errors from ipatests.test_xmlrpc import objectclasses from ipatests.util import ( - assert_equal, assert_not_equal, raises) + assert_deepequal, assert_equal, assert_not_equal, raises) from xmlrpc_test import ( - XMLRPC_test, Declarative, fuzzy_digits, fuzzy_uuid, fuzzy_password, - fuzzy_string, fuzzy_dergeneralizedtime, add_sid, add_oc) + XMLRPC_test, fuzzy_digits, fuzzy_uuid, fuzzy_password, + fuzzy_string, fuzzy_dergeneralizedtime, add_sid, add_oc, raises_exact) from ipapython.dn import DN -import pytest -user1 = u'tuser1' -user2 = u'tuser2' +from ipatests.test_xmlrpc.tracker.base import Tracker +from ipatests.test_xmlrpc.tracker.group_plugin import GroupTracker +from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker + admin1 = u'admin' -admin2 = u'admin2' -renameduser1 = u'tuser' -group1 = u'group1' -admins_group = u'admins' +admin_group = u'admins' invaliduser1 = u'+tuser1' -invaliduser2 = u'tuser1234567890123456789012345678901234567890' +invaliduser2 = u''.join(['a' for n in range(256)]) sshpubkey = (u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGAX3xAeLeaJggwTqMjxNwa6X' - 'HBUAikXPGMzEpVrlLDCZtv00djsFTBi38PkgxBJVkgRWMrcBsr/35lq7P6w8KGI' - 'wA8GI48Z0qBS2NBMJ2u9WQ2hjLN6GdMlo77O0uJY3251p12pCVIS/bHRSq8kHO2' - 'No8g7KA9fGGcagPfQH+ee3t7HUkpbQkFTmbPPN++r3V8oVUk5LxbryB3UIIVzNm' - 'cSIn3JrXynlvui4MixvrtX6zx+O/bBo68o8/eZD26QrahVbA09fivrn/4h3TM01' - '9Eu/c2jOdckfU3cHUV/3Tno5d6JicibyaoDDK7S/yjdn5jhaz8MSEayQvFkZkiF' - '0L public key test') + 'HBUAikXPGMzEpVrlLDCZtv00djsFTBi38PkgxBJVkgRWMrcBsr/35lq7P6w8KGI' + 'wA8GI48Z0qBS2NBMJ2u9WQ2hjLN6GdMlo77O0uJY3251p12pCVIS/bHRSq8kHO2' + 'No8g7KA9fGGcagPfQH+ee3t7HUkpbQkFTmbPPN++r3V8oVUk5LxbryB3UIIVzNm' + 'cSIn3JrXynlvui4MixvrtX6zx+O/bBo68o8/eZD26QrahVbA09fivrn/4h3TM01' + '9Eu/c2jOdckfU3cHUV/3Tno5d6JicibyaoDDK7S/yjdn5jhaz8MSEayQvFkZkiF' + '0L public key test') sshpubkeyfp = (u'13:67:6B:BF:4E:A2:05:8E:AE:25:8B:A1:31:DE:6F:1B ' - 'public key test (ssh-rsa)') + 'public key test (ssh-rsa)') -validlanguage1 = u'en-US;q=0.987 , en, abcdfgh-abcdefgh;q=1 , a;q=1.000' -validlanguage2 = u'*' +validlanguages = { + u'en-US;q=0.987 , en, abcdfgh-abcdefgh;q=1 , a;q=1.000', + u'*' + } -invalidlanguage1 = u'abcdfghji-abcdfghji' -invalidlanguage2 = u'en-us;q=0,123' -invalidlanguage3 = u'en-us;q=0.1234' -invalidlanguage4 = u'en-us;q=1.1' -invalidlanguage5 = u'en-us;q=1.0000' +invalidlanguages = { + u'abcdfghji-abcdfghji', u'en-us;q=0,123', + u'en-us;q=0.1234', u'en-us;q=1.1', u'en-us;q=1.0000' + } principal_expiration_string = "2020-12-07T19:54:13Z" principal_expiration_date = datetime.datetime(2020, 12, 7, 19, 54, 13) @@ -77,6 +78,856 @@ expired_expiration_string = "1991-12-07T19:54:13Z" isodate_re = re.compile('^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$') +@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 user2(request): + tracker = UserTracker(name=u'user2', givenname=u'Test2', sn=u'User2') + return tracker.make_fixture(request) + + +@pytest.fixture(scope='class') +def renameduser(request): + tracker = UserTracker(name=u'ruser1', givenname=u'Ruser', sn=u'Ruser1') + return tracker.make_fixture(request) + + +@pytest.fixture(scope='class') +def admin2(request): + tracker = UserTracker(name=u'admin2', givenname=u'Second', sn=u'Admin') + return tracker.make_fixture(request) + + +@pytest.fixture(scope='class') +def user_npg(request, group): + """ User tracker fixture for testing users with no private group """ + tracker = UserTracker(name=u'npguser1', givenname=u'Npguser', + sn=u'Npguser1', noprivate=True) + tracker.track_create() + del tracker.attrs['mepmanagedentry'] + tracker.attrs.update( + description=[], memberof_group=[group.cn], + objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs') + ) + 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 group(request): + tracker = GroupTracker(name=u'group1') + return tracker.make_fixture(request) + + +@pytest.mark.tier1 +class TestNonexistentUser(XMLRPC_test): + def test_retrieve_nonexistent(self, user): + """ Try to retrieve a non-existent user """ + user.ensure_missing() + command = user.make_retrieve_command() + with raises_exact(errors.NotFound( + reason=u'%s: user not found' % user.uid)): + command() + + def test_update_nonexistent(self, user): + """ Try to update a non-existent user """ + user.ensure_missing() + command = user.make_update_command( + updates=dict(givenname=u'changed')) + with raises_exact(errors.NotFound( + reason=u'%s: user not found' % user.uid)): + command() + + def test_delete_nonexistent(self, user): + """ Try to delete a non-existent user """ + user.ensure_missing() + command = user.make_delete_command() + with raises_exact(errors.NotFound( + reason=u'%s: user not found' % user.uid)): + command() + + def test_rename_nonexistent(self, user, renameduser): + """ Try to rename a non-existent user """ + user.ensure_missing() + command = user.make_update_command( + updates=dict(setattr=u'uid=%s' % renameduser.uid)) + with raises_exact(errors.NotFound( + reason=u'%s: user not found' % user.uid)): + command() + + +@pytest.mark.tier1 +class TestUser(XMLRPC_test): + def test_retrieve(self, user): + """ Create user and try to retrieve it """ + user.retrieve() + + def test_delete(self, user): + """ Delete user """ + user.delete() + + def test_query_status(self, user): + """ Query user_status on a user """ + user.ensure_exists() + result = user.run_command('user_status', user.uid) + assert_deepequal(dict( + count=1, + result=[dict( + dn=user.dn, + krblastfailedauth=[u'N/A'], + krblastsuccessfulauth=[u'N/A'], + krbloginfailedcount=u'0', + now=isodate_re.match, + server=api.env.host, + ), ], + summary=u'Account disabled: False', + truncated=False, + ), result) + user.delete() + + def test_remove_userclass(self, user): + """ Remove attribute userclass from user entry """ + user.ensure_exists() + result = user.run_command( + 'user_mod', user.uid, **dict(userclass=u'') + ) + user.check_update(result) + user.delete() + + +@pytest.mark.tier1 +class TestFind(XMLRPC_test): + def test_find(self, user): + """ Basic check of user-find """ + user.find() + + def test_find_with_all(self, user): + """ Basic check of user-find with --all """ + user.find(all=True) + + def test_find_with_pkey_only(self, user): + """ Basic check of user-find with primary keys only """ + user.ensure_exists() + command = user.make_find_command( + uid=user.uid, pkey_only=True + ) + result = command() + user.check_find(result, pkey_only=True) + + +@pytest.mark.tier1 +class TestActive(XMLRPC_test): + def test_disable(self, user): + """ Disable user using user-disable """ + user.ensure_exists() + user.disable() + command = user.make_retrieve_command() + result = command() + user.check_retrieve(result) + + def test_enable(self, user): + """ Enable user using user-enable """ + user.ensure_exists() + user.enable() + command = user.make_retrieve_command() + result = command() + user.check_retrieve(result) + + def test_disable_using_setattr(self, user): + """ Disable user using setattr """ + user.ensure_exists() + # we need to update the track manually + user.attrs['nsaccountlock'] = True + + command = user.make_update_command( + updates=dict(setattr=u'nsaccountlock=True') + ) + result = command() + user.check_update(result) + + def test_enable_using_setattr(self, user): + """ Enable user using setattr """ + user.ensure_exists() + user.attrs['nsaccountlock'] = False + + command = user.make_update_command( + updates=dict(setattr=u'nsaccountlock=False') + ) + result = command() + user.check_update(result) + + def test_disable_using_usermod(self, user): + """ Disable user using user-mod """ + user.update(dict(nsaccountlock=True), dict(nsaccountlock=True)) + + def test_enable_using_usermod(self, user): + """ Enable user using user-mod """ + user.update(dict(nsaccountlock=False), dict(nsaccountlock=False)) + + +@pytest.mark.tier1 +class TestUpdate(XMLRPC_test): + def test_set_virtual_attribute(self, user): + """ Try to assign an invalid virtual attribute """ + attr = 'random' + user.ensure_exists() + command = user.make_update_command( + updates=dict(setattr=(u'%s=xyz123' % attr)) + ) + with raises_exact(errors.ObjectclassViolation( + info=u'attribute "%s" not allowed' % attr)): + command() + + def test_update(self, user): + """ Update a user attribute """ + user.update(dict(givenname=u'Franta')) + + def test_update_krb_ticket_policy(self, user): + """ Try to update krbmaxticketlife """ + attr = 'krbmaxticketlife' + user.ensure_exists() + command = user.make_update_command( + updates=dict(setattr=(u'%s=88000' % attr)) + ) + with raises_exact(errors.ObjectclassViolation( + info=u'attribute "%s" not allowed' % attr)): + command() + + def test_rename(self, user, renameduser): + """ Rename user and than rename it back """ + user.ensure_exists() + renameduser.ensure_missing() + olduid = user.uid + + # using user.update(dict(uid=value)) results in + # OverlapError: overlapping arguments and options: ['uid'] + user.attrs.update(uid=[renameduser.uid]) + command = user.make_update_command( + updates=dict(setattr=(u'uid=%s' % renameduser.uid)) + ) + result = command() + user.check_update(result) + user.uid = renameduser.uid + + # rename the test user back so it gets properly deleted + user.attrs.update(uid=[olduid]) + command = user.make_update_command( + updates=dict(setattr=(u'uid=%s' % olduid)) + ) + result = command() + user.check_update(result) + user.uid = olduid + + def test_rename_to_the_same_value(self, user): + """ Try to rename user to the same value """ + user.ensure_exists() + command = user.make_update_command( + updates=dict(setattr=(u'uid=%s' % user.uid)) + ) + with raises_exact(errors.EmptyModlist()): + command() + + def test_rename_to_the_same_with_other_mods(self, user): + """ Try to rename user to the same value while + including other modifications that should be done """ + user.ensure_exists() + user.attrs.update(loginshell=[u'/bin/false']) + command = user.make_update_command( + updates=dict(setattr=u'uid=%s' % user.uid, + loginshell=u'/bin/false') + ) + result = command() + user.check_update(result) + + def test_rename_to_too_long_login(self, user): + """ Try to change user login to too long value """ + user.ensure_exists() + command = user.make_update_command( + updates=dict(rename=invaliduser2) + # no exception raised, user is renamed + ) + with raises_exact(errors.ValidationError( + name='rename', + error=u'can be at most 255 characters')): + command() + + def test_update_illegal_ssh_pubkey(self, user): + """ Try to update user with an illegal SSH public key """ + user.ensure_exists() + command = user.make_update_command( + updates=dict(ipasshpubkey=[u"anal nathrach orth' bhais's bethad " + "do che'l de'nmha"]) + ) + with raises_exact(errors.ValidationError( + name='sshpubkey', + error=u'invalid SSH public key')): + command() + + def test_set_ipauserauthtype(self, user): + """ Set ipauserauthtype to 'password' and than back to None """ + user.ensure_exists() + user.update(dict(ipauserauthtype=u'password')) + user.retrieve() + + user.update(dict(ipauserauthtype=None)) + user.delete() + + def test_set_random_password(self, user): + """ Modify user with random password """ + user.ensure_exists() + user.attrs.update( + randompassword=fuzzy_password, + has_keytab=True, + has_password=True + ) + user.update( + dict(random=True), + dict(random=None, randompassword=fuzzy_password) + ) + user.delete() + + def test_rename_to_invalid_login(self, user): + """ Try to change user login to an invalid value """ + user.ensure_exists() + command = user.make_update_command( + updates=dict(rename=invaliduser1) + ) + with raises_exact(errors.ValidationError( + name='rename', + error=u'may only include letters, numbers, _, -, . and $')): + command() + + +@pytest.mark.tier1 +class TestCreate(XMLRPC_test): + def test_create_with_krb_ticket_policy(self): + """ Try to create user with krbmaxticketlife set """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', + sn=u'Tuser1', setattr=u'krbmaxticketlife=88000' + ) + command = testuser.make_create_command() + with raises_exact(errors.ObjectclassViolation( + info=u'attribute "%s" not allowed' % 'krbmaxticketlife')): + command() + + def test_create_with_ssh_pubkey(self): + """ Create user with an assigned SSH public key """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', + sn=u'Tuser1', ipasshpubkey=sshpubkey + ) + testuser.track_create() + # fingerprint is expected in the tracker attrs + testuser.attrs.update(sshpubkeyfp=[sshpubkeyfp]) + command = testuser.make_create_command() + result = command() + testuser.check_create(result) + testuser.delete() + + def test_create_with_invalid_login(self): + """ Try to create user with an invalid login string """ + testuser = UserTracker( + name=invaliduser1, givenname=u'Test', sn=u'User1' + ) + command = testuser.make_create_command() + with raises_exact(errors.ValidationError( + name=u'login', + error=u'may only include letters, numbers, _, -, . and $')): + command() + + def test_create_with_too_long_login(self): + """ Try to create user with too long login string """ + testuser = UserTracker( + name=invaliduser2, givenname=u'Test', sn=u'User1' + ) + command = testuser.make_create_command() + with raises_exact(errors.ValidationError( + name=u'login', + error=u'can be at most 255 characters')): + command() + + def test_create_with_full_address(self): + """ Create user with full address set """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', + street=u'123 Maple Rd', l=u'Anytown', st=u'MD', + postalcode=u'01234-5678', mobile=u'410-555-1212' + ) + testuser.create() + testuser.delete() + + def test_create_with_random_passwd(self): + """ Create user with random password """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', random=True + ) + testuser.track_create() + testuser.attrs.update( + randompassword=fuzzy_password, + has_keytab=True, has_password=True, + krbextradata=[fuzzy_string], + krbpasswordexpiration=[fuzzy_dergeneralizedtime], + krblastpwdchange=[fuzzy_dergeneralizedtime] + ) + command = testuser.make_create_command() + result = command() + testuser.check_create(result) + testuser.delete() + + def test_create_with_different_default_home(self, user): + """ Change default home directory and check that a newly created + user has his home set properly """ + user.ensure_missing() + user.run_command('config_mod', **{u'ipahomesrootdir': u'/other-home'}) + user.track_create() + user.attrs.update(homedirectory=[u'/other-home/%s' % user.name]) + + command = user.make_create_command() + result = command() + user.check_create(result) + user.run_command('config_mod', **{u'ipahomesrootdir': u'/home'}) + user.delete() + + def test_create_with_different_default_shell(self, user): + """ Change default login shell and check that a newly created + user is created with correct login shell value """ + user.ensure_missing() + user.run_command( + 'config_mod', **{u'ipadefaultloginshell': u'/bin/zsh'} + ) + user.track_create() + user.attrs.update(loginshell=[u'/bin/zsh']) + command = user.make_create_command() + result = command() + user.check_create(result) + user.run_command( + 'config_mod', **{u'ipadefaultloginshell': u'/bin/sh'} + ) + user.delete() + + def test_create_without_upg(self): + """ Try to create user without User's Primary GID """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', + noprivate=True + ) + command = testuser.make_create_command() + with raises_exact(errors.NotFound( + reason=u'Default group for new users is not POSIX')): + command() + + def test_create_without_upg_with_gid_set(self): + """ Create user without User's Primary GID with GID set """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', + noprivate=True, gidnumber=1000 + ) + testuser.track_create() + del testuser.attrs['mepmanagedentry'] + testuser.attrs.update(gidnumber=[u'1000']) + testuser.attrs.update( + description=[], + objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs') + ) + command = testuser.make_create_command() + result = command() + testuser.check_create(result, [u'description']) + testuser.delete() + + def test_create_with_uid_999(self): + """ Check that server return uid and gid 999 + when a new client asks for uid 999 """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', uidnumber=999 + ) + testuser.track_create() + testuser.attrs.update( + uidnumber=[u'999'], + gidnumber=[u'999'] + ) + command = testuser.make_create_command() + result = command() + testuser.check_create(result) + testuser.delete() + + def test_create_with_old_DNA_MAGIC_999(self): + """ Check that server picks suitable uid and gid + when an old client asks for the magic uid 999 """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', + uidnumber=999, version=u'2.49' + ) + testuser.track_create() + testuser.attrs.update( + uidnumber=[lambda v: int(v) != 999], + gidnumber=[lambda v: int(v) != 999], + ) + command = testuser.make_create_command() + result = command() + testuser.check_create(result) + testuser.delete() + + def test_create_duplicate(self, user): + """ Try to create second user with the same name """ + user.ensure_exists() + command = user.make_create_command() + with raises_exact(errors.DuplicateEntry( + message=u'user with name "%s" already exists' % + user.uid)): + command() + + def test_create_where_managed_group_exists(self, user, group): + """ Create a managed group and then try to create user + with the same name the group has """ + group.create() + command = user.make_command( + 'user_add', group.cn, **dict(givenname=u'Test', sn=u'User1') + ) + with raises_exact(errors.ManagedGroupExistsError(group=group.cn)): + command() + + +@pytest.mark.tier1 +class TestUserWithGroup(XMLRPC_test): + def test_change_default_user_group(self, group): + """ Change default group for TestUserWithGroup class of tests """ + group.create() + group.run_command( + 'config_mod', **{u'ipadefaultprimarygroup': group.cn} + ) + + def test_create_without_upg(self, user_npg): + """ Try to create user without User's Primary GID + after default group was changed """ + command = user_npg.make_create_command() + # User without private group has some different attrs upon creation + # so we won't use make_create, but do own check instead + # These are set in the fixture + result = command() + user_npg.check_create(result, [u'description', u'memberof_group']) + + def test_create_without_upg_with_gid_set(self, user_npg2): + """ Create user without User's Primary GID with GID set + after default group was changed """ + command = user_npg2.make_create_command() + result = command() + user_npg2.check_create(result, [u'description', u'memberof_group']) + + def test_set_manager(self, user_npg, user_npg2): + """ Update user with own group with manager with own group """ + user_npg.update(dict(manager=user_npg2.uid)) + + def test_check_user_with_renamed_manager(self, user_npg, user_npg2): + """ Rename manager with own group, retrieve user and check + if its manager is also renamed """ + renamed_name = u'renamed_npg2' + old_name = user_npg2.uid + command = user_npg2.make_update_command(dict(rename=renamed_name)) + result = command() + user_npg2.attrs.update(uid=[renamed_name]) + user_npg2.check_update(result) + user_npg.attrs.update(manager=[renamed_name]) + user_npg.retrieve(all=True) + + command = user_npg2.make_command( + 'user_mod', renamed_name, **dict(rename=old_name) + ) + # we rename the user back otherwise the tracker is too confused + result = command() + + def test_check_if_manager_gets_removed(self, user_npg, user_npg2): + """ Delete manager and check if it's gone from user's attributes """ + user_npg2.delete() + del user_npg.attrs[u'manager'] + del user_npg.attrs[u'description'] + user_npg.retrieve(all=True) + + def test_change_default_user_group_back(self, user_npg, user_npg2): + """ Change default group back to 'ipausers' and clean up members """ + user_npg.delete() + user_npg.run_command( + 'config_mod', **{u'ipadefaultprimarygroup': u'ipausers'} + ) + + +@pytest.mark.tier1 +class TestManagers(XMLRPC_test): + def test_assign_nonexistent_manager(self, user, user2): + """ Try to assign user a non-existent manager """ + user.ensure_exists() + user2.ensure_missing() + command = user.make_update_command( + updates=dict(manager=user2.uid) + ) + with raises_exact(errors.NotFound( + reason=u'manager %s not found' % user2.uid)): + command() + + def test_assign_manager(self, user, user2): + """ Make user manager of another user """ + user.ensure_exists() + user2.ensure_exists() + user.update(dict(manager=user2.uid)) + + def test_search_by_manager(self, user, user2): + """ Find user by his manager's UID """ + command = user.make_find_command(manager=user2.uid) + result = command() + user.check_find(result) + + def test_delete_both_user_and_manager(self, user, user2): + """ Delete both user and its manager at once """ + result = user.run_command( + 'user_del', [user.uid, user2.uid], + preserve=False, no_preserve=True + ) + assert_deepequal(dict( + value=[user.uid, user2.uid], + summary=u'Deleted user "%s,%s"' % (user.uid, user2.uid), + result=dict(failed=[]), + ), result) + # mark users as deleted + user.exists = False + user2.exists = False + + +@pytest.mark.tier1 +class TestAdmins(XMLRPC_test): + def test_remove_original_admin(self): + """ Try to remove the only admin """ + tracker = Tracker() + command = tracker.make_command('user_del', [admin1]) + + with raises_exact(errors.LastMemberError( + key=admin1, label=u'group', container=admin_group)): + command() + + def test_disable_original_admin(self): + """ Try to disable the only admin """ + tracker = Tracker() + command = tracker.make_command('user_disable', admin1) + + with raises_exact(errors.LastMemberError( + key=admin1, label=u'group', container=admin_group)): + command() + + def test_create_admin2(self, admin2): + """ Test whether second admin gets created """ + admin2.ensure_exists() + admin2.make_admin() + admin2.delete() + + def test_last_admin_preservation(self, admin2): + """ Create a second admin, disable it. Then try to disable and + remove the original one and receive LastMemberError. Last trial + are these ops with second admin removed. """ + admin2.ensure_exists() + admin2.make_admin() + admin2.disable() + tracker = Tracker() + + with raises_exact(errors.LastMemberError( + key=admin1, label=u'group', container=admin_group)): + tracker.run_command('user_disable', admin1) + with raises_exact(errors.LastMemberError( + key=admin1, label=u'group', container=admin_group)): + tracker.run_command('user_del', admin1) + admin2.delete() + + with raises_exact(errors.LastMemberError( + key=admin1, label=u'group', container=admin_group)): + tracker.run_command('user_disable', admin1) + with raises_exact(errors.LastMemberError( + key=admin1, label=u'group', container=admin_group)): + tracker.run_command('user_del', admin1) + + +@pytest.mark.tier1 +class TestPreferredLanguages(XMLRPC_test): + def test_invalid_preferred_languages(self, user): + """ Try to assign various invalid preferred languages to user """ + user.ensure_exists() + for invalidlanguage in invalidlanguages: + command = user.make_update_command( + dict(preferredlanguage=invalidlanguage) + ) + + with raises_exact(errors.ValidationError( + name='preferredlanguage', + error=(u'must match RFC 2068 - 14.4, e.g., ' + '"da, en-gb;q=0.8, en;q=0.7"') + )): + command() + user.delete() + + def test_valid_preferred_languages(self, user): + """ Update user with different preferred languages """ + for validlanguage in validlanguages: + user.update(dict(preferredlanguage=validlanguage)) + user.delete() + + +@pytest.mark.tier1 +class TestPrincipals(XMLRPC_test): + def test_create_with_bad_realm_in_principal(self): + """ Try to create user with a bad realm in principal """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', + krbprincipalname=u'tuser1@NOTFOUND.ORG' + ) + + command = testuser.make_create_command() + with raises_exact(errors.RealmMismatch()): + command() + + def test_create_with_malformed_principal(self): + """ Try to create user with wrongly formed principal """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', + krbprincipalname=u'tuser1@BAD@NOTFOUND.ORG' + ) + + command = testuser.make_create_command() + with raises_exact(errors.MalformedUserPrincipal( + principal=u'tuser1@BAD@NOTFOUND.ORG')): + command() + + def test_set_principal_expiration(self, user): + """ Set principal expiration for user """ + user.update( + dict(krbprincipalexpiration=principal_expiration_string), + dict(krbprincipalexpiration=[principal_expiration_date]) + ) + + def test_set_invalid_principal_expiration(self, user): + """ Try to set incorrent principal expiration value for user """ + user.ensure_exists() + command = user.make_update_command( + dict(krbprincipalexpiration=invalid_expiration_string) + ) + + with raises_exact(errors.ConversionError( + name='principal_expiration', + error=(u'does not match any of accepted formats: ' + '%Y%m%d%H%M%SZ, %Y-%m-%dT%H:%M:%SZ, %Y-%m-%dT%H:%MZ, ' + '%Y-%m-%dZ, %Y-%m-%d %H:%M:%SZ, %Y-%m-%d %H:%MZ') + )): + command() + + def test_create_with_uppercase_principal(self): + """ Create user with upper-case principal """ + testuser = UserTracker( + name=u'tuser1', givenname=u'Test', sn=u'Tuser1', + krbprincipalname=u'tuser1'.upper() + ) + testuser.create() + testuser.delete() + + +@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_deletes(self): + """ Test that validation is disabled on user deletes """ + tracker = Tracker() + command = tracker.make_command('user_del', invaliduser1) + with raises_exact(errors.NotFound( + reason=u'%s: user not found' % invaliduser1)): + command() + + def test_validation_disabled_on_show(self): + """ Test that validation is disabled on user retrieves """ + tracker = Tracker() + command = tracker.make_command('user_show', invaliduser1) + with raises_exact(errors.NotFound( + reason=u'%s: user not found' % invaliduser1)): + command() + + def test_validation_disabled_on_find(self, user): + """ Test that validation is disabled on user searches """ + result = user.run_command('user_find', invaliduser1) + user.check_find_nomatch(result) + + +@pytest.mark.tier1 +class TestDeniedBindWithExpiredPrincipal(XMLRPC_test): + + password = u'random' + + @classmethod + def setup_class(cls): + super(TestDeniedBindWithExpiredPrincipal, cls).setup_class() + + cls.connection = ldap.initialize('ldap://{host}' + .format(host=api.env.host)) + + @classmethod + def teardown_class(cls): + super(TestDeniedBindWithExpiredPrincipal, cls).teardown_class() + + def test_bind_as_test_user(self, user): + """ Bind as user """ + self.failsafe_add( + api.Object.user, + user.uid, + givenname=u'Test', + sn=u'User1', + userpassword=self.password, + krbprincipalexpiration=principal_expiration_string + ) + + self.connection.simple_bind_s( + str(get_user_dn(user.uid)), self.password + ) + + def test_bind_as_expired_test_user(self, user): + """ Try to bind as expired user """ + api.Command['user_mod']( + user.uid, + krbprincipalexpiration=expired_expiration_string + ) + + raises(ldap.UNWILLING_TO_PERFORM, + self.connection.simple_bind_s, + str(get_user_dn(user.uid)), self.password + ) + + def test_bind_as_renewed_test_user(self, user): + """ Bind as renewed user """ + api.Command['user_mod']( + user.uid, + krbprincipalexpiration=principal_expiration_string + ) + + self.connection.simple_bind_s( + str(get_user_dn(user.uid)), self.password + ) + +# This set of functions (get_*, upg_check, not_upg_check) +# is mostly for legacy purposes here, tests using UserTracker +# should not rely on them + + def get_user_result(uid, givenname, sn, operation='show', omit=[], **overrides): """Get a user result for a user-{add,mod,find,show} command @@ -155,10 +1006,12 @@ def get_admin_result(operation='show', **overrides): def get_user_dn(uid): + """ Get user DN by uid """ return DN(('uid', uid), api.env.container_user, api.env.basedn) def get_group_dn(cn): + """ Get group DN by CN """ return DN(('cn', cn), api.env.container_group, api.env.basedn) @@ -178,1477 +1031,3 @@ def not_upg_check(response): assert_not_equal(response['result']['uidnumber'], response['result']['gidnumber']) return True - - -@pytest.mark.tier1 -class test_user(Declarative): - - cleanup_commands = [ - ('user_del', [user1, user2, renameduser1, admin2], {'continue': True}), - ('group_del', [group1], {}), - ('automember_default_group_remove', [], {'type': u'group'}), - ] - - tests = [ - - dict( - desc='Try to retrieve non-existent "%s"' % user1, - command=('user_show', [user1], {}), - expected=errors.NotFound(reason=u'%s: user not found' % user1), - ), - - - dict( - desc='Try to update non-existent "%s"' % user1, - command=('user_mod', [user1], dict(givenname=u'Foo')), - expected=errors.NotFound(reason=u'%s: user not found' % user1), - ), - - - dict( - desc='Try to delete non-existent "%s"' % user1, - command=('user_del', [user1], {}), - expected=errors.NotFound(reason=u'%s: user not found' % user1), - ), - - - dict( - desc='Try to rename non-existent "%s"' % user1, - command=('user_mod', [user1], - dict(setattr=u'uid=%s' % renameduser1)), - expected=errors.NotFound(reason=u'%s: user not found' % user1), - ), - - - dict( - desc='Create "%s"' % user1, - command=( - 'user_add', - [user1], - dict( - givenname=u'Test', - sn=u'User1', - userclass=u'testusers' - ) - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result( - user1, - u'Test', - u'User1', - 'add', - userclass=[u'testusers'], - objectclass=add_oc( - objectclasses.user, - u'ipantuserattrs' - ) + [u'ipauser'] - ), - ), - extra_check = upg_check, - ), - - - dict( - desc='Try to create duplicate "%s"' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1') - ), - expected=errors.DuplicateEntry( - message=u'user with name "%s" already exists' % user1), - ), - - - dict( - desc='Retrieve "%s"' % user1, - command=( - 'user_show', [user1], {} - ), - expected=dict( - result=get_user_result( - user1, - u'Test', - u'User1', - 'show', - userclass=[u'testusers'] - ), - value=user1, - summary=None, - ), - ), - - dict( - desc='Remove userclass for user "%s"' % user1, - command=('user_mod', [user1], dict(userclass=u'')), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod'), - value=user1, - summary=u'Modified user "%s"' % user1, - ), - ), - - dict( - desc='Search for "%s" with all=True' % user1, - command=( - 'user_find', [user1], {'all': True} - ), - expected=dict( - result=[ - get_user_result( - user1, - u'Test', - u'User1', - 'show-all', - objectclass=add_oc( - objectclasses.user, - u'ipantuserattrs' - ) + [u'ipauser'], - preserved=False - ), - ], - summary=u'1 user matched', - count=1, truncated=False, - ), - ), - - - dict( - desc='Search for "%s" with pkey-only=True' % user1, - command=( - 'user_find', [user1], {'pkey_only': True} - ), - expected=dict( - result=[ - { - 'dn': get_user_dn(user1), - 'uid': [user1], - }, - ], - summary=u'1 user matched', - count=1, truncated=False, - ), - ), - - - dict( - desc='Search for "%s" with minimal attributes' % user1, - command=( - 'user_find', [user1], {} - ), - expected=dict( - result=[ - get_user_result(user1, u'Test', u'User1', 'find'), - ], - summary=u'1 user matched', - count=1, - truncated=False, - ), - ), - - - dict( - desc='Search for all users', - command=( - 'user_find', [], {} - ), - expected=dict( - result=[ - get_admin_result('find'), - get_user_result(user1, u'Test', u'User1', 'find'), - ], - summary=u'2 users matched', - count=2, - truncated=False, - ), - ), - - - dict( - desc='Search for all users with a limit of 1', - command=( - 'user_find', [], dict(sizelimit=1,), - ), - expected=dict( - result=[get_admin_result('find')], - summary=u'1 user matched', - count=1, - truncated=True, - ), - ), - - - dict( - desc='Disable "%s"' % user1, - command=( - 'user_disable', [user1], {} - ), - expected=dict( - result=True, - value=user1, - summary=u'Disabled user account "%s"' % user1, - ), - ), - - dict( - desc='Assert user is disabled', - command=('user_find', [user1], {}), - expected=dict( - result=[get_user_result(user1, u'Test', u'User1', 'find', - nsaccountlock=True)], - summary=u'1 user matched', - count=1, - truncated=False, - ), - ), - - dict( - desc='Enable "%s"' % user1, - command=( - 'user_enable', [user1], {} - ), - expected=dict( - result=True, - value=user1, - summary=u'Enabled user account "%s"' % user1, - ), - ), - - dict( - desc='Assert user "%s" is enabled' % user1, - command=('user_find', [user1], {}), - expected=dict( - result=[get_user_result(user1, u'Test', u'User1', 'find')], - summary=u'1 user matched', - count=1, - truncated=False, - ), - ), - - dict( - desc='Disable "%s" using setattr' % user1, - command=('user_mod', [user1], dict(setattr=u'nsaccountlock=True')), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod', - nsaccountlock=True), - value=user1, - summary=u'Modified user "%s"' % user1, - ), - ), - - dict( - desc='Enable "%s" using setattr' % user1, - command=('user_mod', [user1], dict(setattr=u'nsaccountlock=False')), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod'), - value=user1, - summary=u'Modified user "%s"' % user1, - ), - ), - - dict( - desc='Disable "%s" using user_mod' % user1, - command=('user_mod', [user1], dict(nsaccountlock=True)), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod', - nsaccountlock=True), - value=user1, - summary=u'Modified user "%s"' % user1, - ), - ), - - dict( - desc='Enable "%s" using user_mod' % user1, - command=('user_mod', [user1], dict(nsaccountlock=False)), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod'), - value=user1, - summary=u'Modified user "%s"' % user1, - ), - ), - - dict( - desc='Try setting virtual attribute on "%s" using setattr' % user1, - command=('user_mod', [user1], dict(setattr=u'random=xyz123')), - expected=errors.ObjectclassViolation( - info='attribute "random" not allowed'), - ), - - dict( - desc='Update "%s"' % user1, - command=( - 'user_mod', [user1], dict(givenname=u'Finkle') - ), - expected=dict( - result=get_user_result(user1, u'Finkle', u'User1', 'mod'), - summary=u'Modified user "%s"' % user1, - value=user1, - ), - ), - - - dict( - desc='Try updating the krb ticket policy of "%s"' % user1, - command=( - 'user_mod', [user1], dict(setattr=u'krbmaxticketlife=88000') - ), - expected=errors.ObjectclassViolation( - info=u'attribute "krbmaxticketlife" not allowed'), - ), - - - dict( - desc='Retrieve "%s" to verify update' % user1, - command=('user_show', [user1], {}), - expected=dict( - result=get_user_result(user1, u'Finkle', u'User1', 'show'), - summary=None, - value=user1, - ), - - ), - - - dict( - desc='Rename "%s"' % user1, - command=('user_mod', [user1], - dict(setattr=u'uid=%s' % renameduser1)), - expected=dict( - result=get_user_result( - renameduser1, u'Finkle', u'User1', 'mod', - mail=[u'%s@%s' % (user1, api.env.domain)], - homedirectory=[u'/home/%s' % user1]), - summary=u'Modified user "%s"' % user1, - value=user1, - ), - ), - - - dict( - desc='Rename "%s" to same value' % renameduser1, - command=('user_mod', [renameduser1], - dict(setattr=u'uid=%s' % renameduser1)), - expected=errors.EmptyModlist(), - ), - - dict( - desc='Rename "%s" to same value, check that other modifications ' - 'are performed' % renameduser1, - command=('user_mod', [renameduser1], - dict(setattr=u'uid=%s' % renameduser1, - loginshell=u'/bin/bash')), - expected=dict( - result=get_user_result( - renameduser1, u'Finkle', u'User1', 'mod', - mail=[u'%s@%s' % (user1, api.env.domain)], - homedirectory=[u'/home/%s' % user1], - loginshell=[u'/bin/bash']), - summary=u'Modified user "%s"' % renameduser1, - value=renameduser1, - ), - ), - - - dict( - desc='Rename back "%s"' % renameduser1, - command=('user_mod', [renameduser1], - dict(setattr=u'uid=%s' % user1, loginshell=u'/bin/sh')), - expected=dict( - result=get_user_result(user1, u'Finkle', u'User1', 'mod'), - summary=u'Modified user "%s"' % renameduser1, - value=renameduser1, - ), - ), - - - dict( - desc='Delete "%s"' % user1, - command=('user_del', [user1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user1, - value=[user1], - ), - ), - - - dict( - desc='Try to delete non-existent "%s"' % user1, - command=('user_del', [user1], {}), - expected=errors.NotFound(reason=u'tuser1: user not found'), - ), - - - dict( - desc='Create user "%s" with krb ticket policy' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - setattr=u'krbmaxticketlife=88000') - ), - expected=errors.ObjectclassViolation( - info='attribute "krbmaxticketlife" not allowed'), - ), - - - dict( - desc='Create "%s" with SSH public key' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - ipasshpubkey=[sshpubkey]) - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result( - user1, u'Test', u'User1', 'add', - objectclass=add_oc(objectclasses.user, u'ipantuserattrs'), - ipasshpubkey=[sshpubkey], - sshpubkeyfp=[sshpubkeyfp], - ), - ), - extra_check = upg_check, - ), - - - dict( - desc='Add an illegal SSH public key to "%r"' % user1, - command=('user_mod', [user1], - dict(ipasshpubkey=[u"anal nathrach orth' bhais's bethad " - "do che'l de'nmha"])), - expected=errors.ValidationError(name='sshpubkey', - error=u'invalid SSH public key'), - ), - - - dict( - desc='Delete "%s"' % user1, - command=('user_del', [user1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user1, - value=[user1], - ), - ), - - - dict( - desc='Create "%s"' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1') - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result(user1, u'Test', u'User1', 'add'), - ), - extra_check = upg_check, - ), - - - dict( - desc='Create "%s"' % user2, - command=( - 'user_add', [user2], dict(givenname=u'Test', sn=u'User2') - ), - expected=dict( - value=user2, - summary=u'Added user "%s"' % user2, - result=get_user_result(user2, u'Test', u'User2', 'add'), - ), - extra_check = upg_check, - ), - - - dict( - desc='Make non-existent "%s" the manager of "%s"' % (renameduser1, - user2), - command=('user_mod', [user2], dict(manager=renameduser1)), - expected=errors.NotFound( - reason=u'manager %s not found' % renameduser1), - ), - - - dict( - desc='Make "%s" the manager of "%s"' % (user1, user2), - command=('user_mod', [user2], dict(manager=user1)), - expected=dict( - result=get_user_result(user2, u'Test', u'User2', 'mod', - manager=[user1]), - summary=u'Modified user "%s"' % user2, - value=user2, - ), - ), - - dict( - desc='Search for "%s" with manager "%s"' % (user2, user1), - command=( - 'user_find', [user2], {'manager': user1} - ), - expected=dict( - result=[get_user_result(user2, u'Test', u'User2', 'find', - manager=[user1])], - summary=u'1 user matched', - count=1, - truncated=False, - ), - ), - - dict( - desc='Delete "%s" and "%s" at the same time' % (user1, user2), - command=('user_del', [user1, user2], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "tuser1,tuser2"', - value=[user1, user2], - ), - ), - - dict( - desc='Try to retrieve non-existent "%s"' % user1, - command=('user_show', [user1], {}), - expected=errors.NotFound(reason=u'%s: user not found' % user1), - ), - - - dict( - desc='Try to update non-existent "%s"' % user1, - command=('user_mod', [user1], dict(givenname=u'Foo')), - expected=errors.NotFound(reason=u'%s: user not found' % user1), - ), - - - dict( - desc='Test an invalid login name "%s"' % invaliduser1, - command=('user_add', [invaliduser1], dict(givenname=u'Test', - sn=u'User1')), - expected=errors.ValidationError(name='login', - error=u'may only include letters, numbers, _, -, . and $'), - ), - - - dict( - desc='Test a login name that is too long "%s"' % invaliduser2, - command=('user_add', [invaliduser2], - dict(givenname=u'Test', sn=u'User1')), - expected=errors.ValidationError(name='login', - error='can be at most 32 characters'), - ), - - - # The assumption on these next 4 tests is that if we don't get a - # validation error then the request was processed normally. - dict( - desc='Test that validation is disabled on deletes', - command=('user_del', [invaliduser1], {}), - expected=errors.NotFound( - reason=u'%s: user not found' % invaliduser1), - ), - - - dict( - desc='Test that validation is disabled on show', - command=('user_show', [invaliduser1], {}), - expected=errors.NotFound( - reason=u'%s: user not found' % invaliduser1), - ), - - - dict( - desc='Test that validation is disabled on find', - command=('user_find', [invaliduser1], {}), - expected=dict( - count=0, - truncated=False, - summary=u'0 users matched', - result=[], - ), - ), - - - dict( - desc='Try to rename to invalid username "%s"' % user1, - command=('user_mod', [user1], dict(rename=invaliduser1)), - expected=errors.ValidationError(name='rename', - error=u'may only include letters, numbers, _, -, . and $'), - ), - - - dict( - desc='Try to rename to a username that is too long "%s"' % user1, - command=('user_mod', [user1], dict(rename=invaliduser2)), - expected=errors.ValidationError(name='login', - error='can be at most 32 characters'), - ), - - - dict( - desc='Create "%s"' % group1, - command=( - 'group_add', [group1], dict(description=u'Test desc') - ), - expected=dict( - value=group1, - summary=u'Added group "%s"' % group1, - result=dict( - cn=[group1], - description=[u'Test desc'], - gidnumber=[fuzzy_digits], - objectclass=objectclasses.group + [u'posixgroup'], - ipauniqueid=[fuzzy_uuid], - dn=get_group_dn(group1), - ), - ), - ), - - - dict( - desc='Try to user "%s" where the managed group exists' % group1, - command=( - 'user_add', [group1], dict(givenname=u'Test', sn=u'User1') - ), - expected=errors.ManagedGroupExistsError(group=group1) - ), - - - dict( - desc='Create "%s" with a full address' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - street=u'123 Maple Rd', l=u'Anytown', st=u'MD', - telephonenumber=u'410-555-1212', postalcode=u'01234-5678') - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result( - user1, u'Test', u'User1', 'add', - street=[u'123 Maple Rd'], l=[u'Anytown'], st=[u'MD'], - telephonenumber=[u'410-555-1212'], - postalcode=[u'01234-5678'], - ), - ), - ), - - - dict( - desc='Delete "%s"' % user1, - command=('user_del', [user1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user1, - value=[user1], - ), - ), - - dict( - desc='Create "%s" with random password' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - random=True) - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result( - user1, u'Test', u'User1', 'add', - randompassword=fuzzy_password, - has_keytab=True, has_password=True, - krbextradata=[fuzzy_string], - krbpasswordexpiration=[fuzzy_dergeneralizedtime], - krblastpwdchange=[fuzzy_dergeneralizedtime] - ), - ), - ), - - dict( - desc='Delete "%s"' % user1, - command=('user_del', [user1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user1, - value=[user1], - ), - ), - - dict( - desc='Create "%s"' % user2, - command=( - 'user_add', [user2], dict(givenname=u'Test', sn=u'User2') - ), - expected=dict( - value=user2, - summary=u'Added user "%s"' % user2, - result=get_user_result(user2, u'Test', u'User2', 'add'), - ), - ), - - dict( - desc='Modify "%s" with random password' % user2, - command=( - 'user_mod', [user2], dict(random=True) - ), - expected=dict( - result=get_user_result( - user2, u'Test', u'User2', 'mod', - randompassword=fuzzy_password, - has_keytab=True, has_password=True, - ), - summary=u'Modified user "%s"' % user2, - value=user2, - ), - ), - - dict( - desc='Delete "%s"' % user2, - command=('user_del', [user2], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user2, - value=[user2], - ), - ), - - dict( - desc='Create user "%s" with upper-case principal' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - krbprincipalname=user1.upper()) - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result(user1, u'Test', u'User1', 'add'), - ), - ), - - - dict( - desc='Create user "%s" with bad realm in principal' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - krbprincipalname='%s@NOTFOUND.ORG' % user1) - ), - expected=errors.RealmMismatch() - ), - - - dict( - desc='Create user "%s" with malformed principal' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - krbprincipalname='%s@BAD@NOTFOUND.ORG' % user1) - ), - expected=errors.MalformedUserPrincipal( - principal='%s@BAD@NOTFOUND.ORG' % user1), - ), - - dict( - desc='Delete "%s"' % user1, - command=('user_del', [user1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user1, - value=[user1], - ), - ), - - dict( - desc='Change default home directory', - command=( - 'config_mod', [], dict(ipahomesrootdir=u'/other-home'), - ), - expected=lambda x, output: x is None, - ), - - dict( - desc=('Create user "%s" with different default ' - 'home directory' % user1), - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1') - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result(user1, u'Test', u'User1', 'add', - homedirectory=[u'/other-home/tuser1']), - ), - ), - - - dict( - desc='Reset default home directory', - command=( - 'config_mod', [], dict(ipahomesrootdir=u'/home'), - ), - expected=lambda x, output: x is None, - ), - - dict( - desc='Delete "%s"' % user1, - command=('user_del', [user1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user1, - value=[user1], - ), - ), - - dict( - desc='Change default login shell', - command=( - 'config_mod', [], - dict(ipadefaultloginshell=u'/usr/bin/ipython'), - ), - expected=lambda x, output: x is None, - ), - - dict( - desc='Create user "%s" with different default login shell' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1') - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result(user1, u'Test', u'User1', 'add', - loginshell=[u'/usr/bin/ipython']), - ), - ), - - dict( - desc='Reset default login shell', - command=( - 'config_mod', [], dict(ipadefaultloginshell=u'/bin/sh'), - ), - expected=lambda x, output: x is None, - ), - - dict( - desc='Delete "%s"' % user1, - command=('user_del', [user1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user1, - value=[user1], - ), - ), - - dict( - desc='Create "%s" without UPG' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - noprivate=True) - ), - expected=errors.NotFound( - reason='Default group for new users is not POSIX'), - ), - - dict( - desc='Create "%s" without UPG with GID explicitly set' % user2, - command=( - 'user_add', [user2], dict(givenname=u'Test', sn=u'User2', - noprivate=True, gidnumber=1000) - ), - expected=dict( - value=user2, - summary=u'Added user "%s"' % user2, - result=get_user_result( - user2, u'Test', u'User2', 'add', - objectclass=add_oc(objectclasses.user_base, - u'ipantuserattrs'), - gidnumber=[u'1000'], - description=[], - omit=['mepmanagedentry'], - ), - ), - ), - - dict( - desc='Delete "%s"' % user2, - command=('user_del', [user2], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user2, - value=[user2], - ), - ), - - dict( - desc='Change default user group', - command=( - 'config_mod', [], dict(ipadefaultprimarygroup=group1), - ), - expected=lambda x, output: x is None, - ), - - dict( - desc='Create "%s" without UPG' % user1, - command=( - 'user_add', [user1], dict(givenname=u'Test', sn=u'User1', - noprivate=True) - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result( - user1, u'Test', u'User1', 'add', - objectclass=add_oc(objectclasses.user_base, - u'ipantuserattrs'), - description=[], - memberof_group=[group1], - omit=['mepmanagedentry'], - ), - ), - extra_check = not_upg_check, - ), - - dict( - desc='Create "%s" without UPG with GID explicitly set' % user2, - command=( - 'user_add', [user2], dict(givenname=u'Test', sn=u'User2', - noprivate=True, gidnumber=1000) - ), - expected=dict( - value=user2, - summary=u'Added user "%s"' % user2, - result=get_user_result( - user2, u'Test', u'User2', 'add', - objectclass=add_oc(objectclasses.user_base, - u'ipantuserattrs'), - description=[], - gidnumber=[u'1000'], - memberof_group=[group1], - omit=['mepmanagedentry'], - ), - ), - ), - - dict( - desc='Set %r as manager of %r' % (user1, user2), - command=( - 'user_mod', [user2], dict(manager=user1) - ), - expected=dict( - result=get_user_result(user2, u'Test', u'User2', 'mod', - gidnumber=[u'1000'], - memberof_group=[group1], - manager=[user1]), - summary=u'Modified user "%s"' % user2, - value=user2, - ), - ), - - dict( - desc='Rename "%s"' % user1, - command=('user_mod', [user1], dict(rename=renameduser1)), - expected=dict( - result=get_user_result( - renameduser1, u'Test', u'User1', 'mod', - homedirectory=[u'/home/%s' % user1], - mail=[u'%s@%s' % (user1, api.env.domain)], - memberof_group=[group1], - ), - summary=u'Modified user "%s"' % user1, - value=user1, - ), - ), - - dict( - desc='Retrieve %r and check that manager is renamed' % user2, - command=( - 'user_show', [user2], {'all': True} - ), - expected=dict( - result=get_user_result( - user2, u'Test', u'User2', 'show-all', - gidnumber=[u'1000'], - memberof_group=[group1], - manager=[renameduser1], - objectclass=add_oc(objectclasses.user_base, - u'ipantuserattrs'), - preserved=False, - omit=['mepmanagedentry'], - ), - value=user2, - summary=None, - ), - ), - - dict( - desc='Delete %r' % renameduser1, - command=('user_del', [renameduser1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % renameduser1, - value=[renameduser1], - ), - ), - - dict( - desc='Retrieve %r and check that manager is gone' % user2, - command=( - 'user_show', [user2], {'all': True} - ), - expected=dict( - result=get_user_result( - user2, u'Test', u'User2', 'show-all', - gidnumber=[u'1000'], - memberof_group=[group1], - objectclass=add_oc(objectclasses.user_base, - u'ipantuserattrs'), - preserved=False, - omit=['mepmanagedentry'], - ), - value=user2, - summary=None, - ), - ), - - dict( - desc='Reset default user group', - command=( - 'config_mod', [], dict(ipadefaultprimarygroup=u'ipausers'), - ), - expected=lambda x, output: x is None, - ), - - dict( - desc='Try to remove the original admin user "%s"' % admin1, - command=('user_del', [admin1], {}), - expected=errors.LastMemberError(key=admin1, label=u'group', - container=admins_group), - ), - - dict( - desc='Try to disable the original admin user "%s"' % admin1, - command=('user_disable', [admin1], {}), - expected=errors.LastMemberError(key=admin1, label=u'group', - container=admins_group), - ), - - - dict( - desc='Create 2nd admin user "%s"' % admin2, - command=( - 'user_add', [admin2], dict(givenname=u'Second', sn=u'Admin') - ), - expected=dict( - value=admin2, - summary=u'Added user "%s"' % admin2, - result=get_user_result(admin2, u'Second', u'Admin', 'add'), - ), - ), - - dict( - desc='Add "%s" to the admins group "%s"' % (admin2, admins_group), - command=('group_add_member', [admins_group], dict(user=admin2)), - expected=dict( - completed=1, - failed=dict( - member=dict( - group=tuple(), - user=tuple(), - ), - ), - result={ - 'dn': get_group_dn(admins_group), - 'member_user': [admin1, admin2], - 'gidnumber': [fuzzy_digits], - 'cn': [admins_group], - 'description': [u'Account administrators group'], - }, - ), - ), - - - dict( - desc=('Retrieve admins group "%s" to verify membership is ' - '"%s","%s"' % (admins_group, admin1, admin2)), - command=('group_show', [admins_group], {}), - expected=dict( - value=admins_group, - result=dict( - cn=[admins_group], - gidnumber=[fuzzy_digits], - description=[u'Account administrators group'], - dn=get_group_dn(admins_group), - member_user=[admin1, admin2], - ), - summary=None, - ), - ), - - dict( - desc=('Disable 2nd admin user "%s", admins group "%s" should also ' - 'contain enabled "%s"' % (admin2, admins_group, admin1)), - command=( - 'user_disable', [admin2], {} - ), - expected=dict( - result=True, - value=admin2, - summary=u'Disabled user account "%s"' % admin2, - ), - ), - - dict( - desc='Assert 2nd admin user "%s" is disabled' % admin2, - command=('user_find', [admin2], {}), - expected=dict( - result=[lambda d: d['nsaccountlock'] is True], - summary=u'1 user matched', - count=1, - truncated=False, - ), - ), - - dict( - desc='Try to disable the origin admin user "%s"' % admin1, - command=('user_disable', [admin1], {}), - expected=errors.LastMemberError(key=admin1, label=u'group', - container=admins_group), - ), - - dict( - desc='Try to remove the original admin user "%s"' % admin1, - command=('user_del', [admin1], {}), - expected=errors.LastMemberError(key=admin1, label=u'group', - container=admins_group), - ), - - dict( - desc='Delete 2nd admin "%s"' % admin2, - command=('user_del', [admin2], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % admin2, - value=[admin2], - ), - ), - - dict( - desc=('Retrieve admins group "%s" to verify membership is "%s"' - % (admins_group, admin1)), - command=('group_show', [admins_group], {}), - expected=dict( - value=admins_group, - result=dict( - cn=[admins_group], - gidnumber=[fuzzy_digits], - description=[u'Account administrators group'], - dn=get_group_dn(admins_group), - member_user=[admin1], - ), - summary=None, - ), - ), - - dict( - desc='Assert original admin user "%s" is enabled' % admin1, - command=('user_find', [admin1], {}), - expected=dict( - result=[lambda d: d['nsaccountlock'] is False], - summary=u'1 user matched', - count=1, - truncated=False, - ), - ), - - dict( - desc='Try to remove the original admin user "%s"' % admin1, - command=('user_del', [admin1], {}), - expected=errors.LastMemberError(key=admin1, label=u'group', - container=admins_group), - ), - - dict( - desc='Try to disable the original admin user "%s"' % admin1, - command=('user_disable', [admin1], {}), - expected=errors.LastMemberError(key=admin1, label=u'group', - container=admins_group), - ), - - dict( - desc='Set default automember group for groups as ipausers', - command=( - 'automember_default_group_set', [], dict( - type=u'group', - automemberdefaultgroup=u'ipausers' - ) - ), - expected=dict( - result=dict( - cn=[u'Group'], - automemberdefaultgroup=[DN(('cn', 'ipausers'), - ('cn', 'groups'), - ('cn', 'accounts'), - api.env.basedn)], - ), - value=u'group', - summary=u'Set default (fallback) group for automember "group"', - ), - ), - - dict( - desc='Delete "%s"' % user2, - command=('user_del', [user2], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user2, - value=[user2], - ), - ), - - dict( - desc='Create %r' % user2, - command=( - 'user_add', [user2], dict(givenname=u'Test', sn=u'User2') - ), - expected=dict( - value=user2, - summary=u'Added user "tuser2"', - result=get_user_result(user2, u'Test', u'User2', 'add'), - ), - ), - - dict( - desc='Create "%s" with UID 999' % user1, - command=( - 'user_add', [user1], dict( - givenname=u'Test', sn=u'User1', uidnumber=999) - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result(user1, u'Test', u'User1', 'add', - uidnumber=[u'999'], - gidnumber=[u'999']), - ), - extra_check = upg_check, - ), - - dict( - desc='Delete "%s"' % user1, - command=('user_del', [user1], {}), - expected=dict( - result=dict(failed=[]), - summary=u'Deleted user "%s"' % user1, - value=[user1], - ), - ), - - dict( - desc='Create "%s" with old DNA_MAGIC uid 999' % user1, - command=( - 'user_add', [user1], dict( - givenname=u'Test', sn=u'User1', uidnumber=999, - version=u'2.49') - ), - expected=dict( - value=user1, - summary=u'Added user "%s"' % user1, - result=get_user_result( - user1, u'Test', u'User1', 'add', - uidnumber=[lambda v: int(v) != 999], - gidnumber=[lambda v: int(v) != 999], - ), - ), - extra_check = upg_check, - ), - - dict( - desc='Set ipauserauthtype for "%s"' % user1, - command=('user_mod', [user1], dict(ipauserauthtype=u'password')), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod', - ipauserauthtype=[u'password'], - ), - value=user1, - summary='Modified user "%s"' % user1, - ), - ), - - dict( - desc='Retrieve "%s" to verify ipauserauthtype' % user1, - command=('user_show', [user1], {}), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'show', - ipauserauthtype=[u'password'], - ), - value=user1, - summary=None, - ), - ), - - dict( - desc='Unset ipauserauthtype for "%s"' % user1, - command=('user_mod', [user1], dict(ipauserauthtype=None)), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod'), - value=user1, - summary='Modified user "%s"' % user1, - ), - ), - - dict( - desc='Query status of "%s"' % user1, - command=('user_status', [user1], {}), - expected=dict( - count=1, - result=[ - dict( - dn=get_user_dn(user1), - krblastfailedauth=[u'N/A'], - krblastsuccessfulauth=[u'N/A'], - krbloginfailedcount=u'0', - now=isodate_re.match, - server=api.env.host, - ), - ], - summary=u'Account disabled: False', - truncated=False, - ), - ), - - dict( - desc='Test an invalid preferredlanguage "%s"' % invalidlanguage1, - command=('user_mod', [user1], - dict(preferredlanguage=invalidlanguage1)), - expected=errors.ValidationError(name='preferredlanguage', - error=(u'must match RFC 2068 - 14.4, e.g., ' - '"da, en-gb;q=0.8, en;q=0.7"')), - ), - - dict( - desc='Test an invalid preferredlanguage "%s"' % invalidlanguage2, - command=('user_mod', [user1], - dict(preferredlanguage=invalidlanguage2)), - expected=errors.ValidationError(name='preferredlanguage', - error=(u'must match RFC 2068 - 14.4, e.g., ' - '"da, en-gb;q=0.8, en;q=0.7"')), - ), - - dict( - desc='Test an invalid preferredlanguage "%s"' % invalidlanguage3, - command=('user_mod', [user1], - dict(preferredlanguage=invalidlanguage3)), - expected=errors.ValidationError(name='preferredlanguage', - error=(u'must match RFC 2068 - 14.4, e.g., ' - '"da, en-gb;q=0.8, en;q=0.7"')), - ), - - dict( - desc='Test an invalid preferredlanguage "%s"' % invalidlanguage4, - command=('user_mod', [user1], - dict(preferredlanguage=invalidlanguage4)), - expected=errors.ValidationError(name='preferredlanguage', - error=(u'must match RFC 2068 - 14.4, e.g., ' - '"da, en-gb;q=0.8, en;q=0.7"')), - ), - - dict( - desc='Test an invalid preferredlanguage "%s"' % invalidlanguage5, - command=('user_mod', [user1], - dict(preferredlanguage=invalidlanguage5)), - expected=errors.ValidationError(name='preferredlanguage', - error=(u'must match RFC 2068 - 14.4, e.g., ' - '"da, en-gb;q=0.8, en;q=0.7"')), - ), - - dict( - desc='Set preferredlanguage "%s"' % validlanguage1, - command=('user_mod', [user1], - dict(preferredlanguage=validlanguage1)), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod', - preferredlanguage=[validlanguage1], - ), - value=user1, - summary='Modified user "%s"' % user1, - ), - ), - - dict( - desc='Set preferredlanguage "%s"' % validlanguage2, - command=('user_mod', [user1], - dict(preferredlanguage=validlanguage2)), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod', - preferredlanguage=[validlanguage2], - ), - value=user1, - summary='Modified user "%s"' % user1, - ), - ), - - dict( - desc='Set principal expiration "%s"' % principal_expiration_string, - command=('user_mod', [user1], - dict(krbprincipalexpiration=principal_expiration_string)), - expected=dict( - result=get_user_result(user1, u'Test', u'User1', 'mod', - krbprincipalexpiration=[principal_expiration_date], - ), - value=user1, - summary='Modified user "%s"' % user1, - ), - ), - - dict( - desc='Set principal expiration "%s"' % invalid_expiration_string, - command=('user_mod', [user1], - dict(krbprincipalexpiration=invalid_expiration_string)), - expected=errors.ConversionError(name='principal_expiration', - error=(u'does not match any of accepted formats: ' - '%Y%m%d%H%M%SZ, %Y-%m-%dT%H:%M:%SZ, %Y-%m-%dT%H:%MZ, ' - '%Y-%m-%dZ, %Y-%m-%d %H:%M:%SZ, %Y-%m-%d %H:%MZ') - ), - ), - - ] - - -@pytest.mark.tier1 -class test_denied_bind_with_expired_principal(XMLRPC_test): - - password = u'random' - - @classmethod - def setup_class(cls): - super(test_denied_bind_with_expired_principal, cls).setup_class() - - cls.connection = ldap.initialize('ldap://{host}' - .format(host=api.env.host)) - - @classmethod - def teardown_class(cls): - cls.failsafe_del(api.Object.user, user1) - super(test_denied_bind_with_expired_principal, cls).teardown_class() - - def test_1_bind_as_test_user(self): - self.failsafe_add( - api.Object.user, - user1, - givenname=u'Test', - sn=u'User1', - userpassword=self.password, - krbprincipalexpiration=principal_expiration_string - ) - - self.connection.simple_bind_s(str(get_user_dn(user1)), self.password) - - def test_2_bind_as_expired_test_user(self): - api.Command['user_mod']( - user1, - krbprincipalexpiration=expired_expiration_string) - - raises(ldap.UNWILLING_TO_PERFORM, - self.connection.simple_bind_s, - str(get_user_dn(user1)), self.password) - - def test_3_bind_as_renewed_test_user(self): - api.Command['user_mod']( - user1, - krbprincipalexpiration=principal_expiration_string) - - self.connection.simple_bind_s(str(get_user_dn(user1)), self.password) diff --git a/ipatests/test_xmlrpc/tracker/user_plugin.py b/ipatests/test_xmlrpc/tracker/user_plugin.py index 2e042c60e..c72e17424 100644 --- a/ipatests/test_xmlrpc/tracker/user_plugin.py +++ b/ipatests/test_xmlrpc/tracker/user_plugin.py @@ -29,28 +29,31 @@ class UserTracker(Tracker): u'krbprincipalexpiration', u'usercertificate', u'dn', u'has_keytab', u'has_password', u'street', u'postalcode', u'facsimiletelephonenumber', u'carlicense', u'ipasshpubkey', u'sshpubkeyfp', u'nsaccountlock', - u'preserved', u'memberof_group', u'l', u'mobile', u'krbextradata', - u'krblastpwdchange', u'krbpasswordexpiration', u'pager', u'st' - } + u'memberof_group', u'l', u'mobile', u'krbextradata', + u'krblastpwdchange', u'krbpasswordexpiration', u'pager', u'st', + u'manager', u'preserved'} retrieve_all_keys = retrieve_keys | { u'cn', u'ipauniqueid', u'objectclass', u'mepmanagedentry', - u'displayname', u'gecos', u'initials', u'krbprincipalname', u'manager'} + u'displayname', u'gecos', u'initials', u'krbprincipalname', + u'preserved'} retrieve_preserved_keys = retrieve_keys - {u'memberof_group'} retrieve_preserved_all_keys = retrieve_all_keys - {u'memberof_group'} create_keys = retrieve_all_keys | { - u'randompassword', u'mepmanagedentry', u'krbextradata', u'krbpasswordexpiration', u'krblastpwdchange', - u'krbprincipalkey', u'randompassword', u'userpassword' - } + u'krbprincipalkey', u'userpassword', u'randompassword'} + create_keys = create_keys - {u'nsaccountlock'} + update_keys = retrieve_keys - {u'dn'} activate_keys = retrieve_all_keys - {u'has_keytab', u'has_password', u'nsaccountlock', u'sshpubkeyfp'} find_keys = retrieve_keys - {u'mepmanagedentry', u'memberof_group'} - find_all_keys = retrieve_all_keys - {u'mepmanagedentry', u'memberof_group'} + find_all_keys = retrieve_all_keys + + primary_keys = {u'uid', u'dn'} def __init__(self, name, givenname, sn, **kwargs): super(UserTracker, self).__init__(default_version=None) @@ -108,11 +111,18 @@ class UserTracker(Tracker): """ Make function that enables user using user-enable """ return self.make_command('user_enable', self.uid) + def make_disable_command(self): + """ Make function that disables user using user-disable """ + return self.make_command('user_disable', self.uid) + def make_stage_command(self): """ Make function that restores preserved user by moving it to staged container """ return self.make_command('user_stage', self.uid) + def make_group_add_member_command(self, *args, **kwargs): + return self.make_command('group_add_member', *args, **kwargs) + def track_create(self): """ Update expected state for user creation """ self.attrs = dict( @@ -137,25 +147,71 @@ class UserTracker(Tracker): has_password=False, mepmanagedentry=[get_group_dn(self.uid)], memberof_group=[u'ipausers'], + nsaccountlock=[u'false'], ) for key in self.kwargs: if key == u'krbprincipalname': - self.attrs[key] = [u'%s@%s' % ( - (self.kwargs[key].split('@'))[0].lower(), - (self.kwargs[key].split('@'))[1] + try: + self.attrs[key] = [u'%s@%s' % ( + (self.kwargs[key].split('@'))[0].lower(), + (self.kwargs[key].split('@'))[1] + )] + except IndexError as ex: + # we can provide just principal part + self.attrs[key] = [u'%s@%s' % ( + (self.kwargs[key].lower(), + self.api.env.realm) )] else: - self.attrs[key] = [self.kwargs[key]] + if not type(self.kwargs[key]) is list: + self.attrs[key] = [self.kwargs[key]] + else: + self.attrs[key] = self.kwargs[key] self.exists = True - def check_create(self, result): + def update(self, updates, expected_updates=None): + """Helper function to update this user and check the result + + Overriding Tracker method for setting self.attrs correctly; + * most attributes stores its value in list + * the rest can be overridden by expected_updates + * allow deleting parametrs if update value is None + """ + if expected_updates is None: + expected_updates = {} + + self.ensure_exists() + command = self.make_update_command(updates) + result = command() + + for key, value in updates.items(): + if value is None or value is '' or value is u'': + del self.attrs[key] + else: + if type(value) is list: + self.attrs[key] = value + else: + self.attrs[key] = [value] + for key, value in expected_updates.items(): + if value is None or value is '' or value is u'': + del self.attrs[key] + else: + self.attrs[key] = value + + self.check_update( + result, + extra_keys=set(updates.keys()) | set(expected_updates.keys()) + ) + + def check_create(self, result, extra_keys=()): """ Check 'user-add' command result """ + expected = self.filter_attrs(self.create_keys | set(extra_keys)) assert_deepequal(dict( value=self.uid, summary=u'Added user "%s"' % self.uid, - result=self.filter_attrs(self.create_keys), + result=self.filter_attrs(expected), ), result) def check_delete(self, result): @@ -166,9 +222,8 @@ class UserTracker(Tracker): result=dict(failed=[]), ), result) - def check_retrieve(self, result, all=False): + def check_retrieve(self, result, all=False, raw=False): """ Check 'user-show' command result """ - if u'preserved' in self.attrs and self.attrs[u'preserved']: self.retrieve_all_keys = self.retrieve_preserved_all_keys self.retrieve_keys = self.retrieve_preserved_keys @@ -195,16 +250,26 @@ class UserTracker(Tracker): result=expected, ), result) - def check_find(self, result, all=False, raw=False): + def check_find(self, result, all=False, pkey_only=False, raw=False): """ Check 'user-find' command result """ - self.attrs[u'nsaccountlock'] = True - self.attrs[u'preserved'] = True - if all: + if u'preserved' not in self.attrs: + self.attrs.update(preserved=False) expected = self.filter_attrs(self.find_all_keys) + elif pkey_only: + expected = self.filter_attrs(self.primary_keys) else: expected = self.filter_attrs(self.find_keys) + if all and self.attrs[u'preserved']: + del expected[u'mepmanagedentry'] + + if u'nsaccountlock' in expected: + if expected[u'nsaccountlock'] == [u'true']: + expected[u'nsaccountlock'] = True + elif expected[u'nsaccountlock'] == [u'false']: + expected[u'nsaccountlock'] = False + assert_deepequal(dict( count=1, truncated=False, @@ -223,10 +288,32 @@ class UserTracker(Tracker): def check_update(self, result, extra_keys=()): """ Check 'user-mod' command result """ + expected = self.filter_attrs(self.update_keys | set(extra_keys)) + if expected[u'nsaccountlock'] == [u'true']: + expected[u'nsaccountlock'] = True + elif expected[u'nsaccountlock'] == [u'false']: + expected[u'nsaccountlock'] = False + assert_deepequal(dict( value=self.uid, summary=u'Modified user "%s"' % self.uid, - result=self.filter_attrs(self.update_keys | set(extra_keys)) + result=expected + ), result) + + def check_enable(self, result): + """ Check result of enable user operation """ + assert_deepequal(dict( + value=self.name, + summary=u'Enabled user account "%s"' % self.name, + result=True + ), result) + + def check_disable(self, result): + """ Check result of disable user operation """ + assert_deepequal(dict( + value=self.name, + summary=u'Disabled user account "%s"' % self.name, + result=True ), result) def create_from_staged(self, stageduser): @@ -287,6 +374,22 @@ class UserTracker(Tracker): result=True ), result) + def enable(self): + """ Enable user account if it was disabled """ + if (self.attrs['nsaccountlock'] is True or + self.attrs['nsaccountlock'] == [u'true']): + self.attrs.update(nsaccountlock=False) + result = self.make_enable_command()() + self.check_enable(result) + + def disable(self): + """ Disable user account if it was enabled """ + if (self.attrs['nsaccountlock'] is False or + self.attrs['nsaccountlock'] == [u'false']): + self.attrs.update(nsaccountlock=True) + result = self.make_disable_command()() + self.check_disable(result) + def track_delete(self, preserve=False): """Update expected state for host deletion""" if preserve: @@ -348,3 +451,27 @@ class UserTracker(Tracker): request.addfinalizer(finish) return self + + def make_admin(self, admin_group=u'admins'): + """ Add user to the administrator's group """ + result = self.run_command('group_show', admin_group) + admin_group_content = result[u'result'][u'member_user'] + admin_group_expected = list(admin_group_content) + [self.name] + + command = self.make_group_add_member_command( + admin_group, **dict(user=self.name) + ) + result = command() + assert_deepequal(dict( + completed=1, + failed=dict( + member=dict(group=tuple(), user=tuple()) + ), + result={ + 'dn': get_group_dn(admin_group), + 'member_user': admin_group_expected, + 'gidnumber': [fuzzy_digits], + 'cn': [admin_group], + 'description': [u'Account administrators group'], + }, + ), result) |