Hi, sending corrected version.
F. On Thu, 12 Nov 2015 14:03:19 +0100 Milan Kubík <mku...@redhat.com> wrote: > On 11/10/2015 12:13 PM, Filip Škola wrote: > > Hi, > > > > fixed. > > > > F. > > > > On Tue, 10 Nov 2015 10:52:45 +0100 > > Milan Kubík <mku...@redhat.com> wrote: > > > >> On 11/09/2015 04:35 PM, Filip Škola wrote: > >>> Another patch was applied in the meantime. > >>> > >>> Attaching an updated version. > >>> > >>> F. > >>> > >>> On Mon, 9 Nov 2015 13:35:02 +0100 > >>> Milan Kubík <mku...@redhat.com> wrote: > >>> > >>>> On 11/06/2015 11:32 AM, Filip Škola wrote: > >>>> Hi, > >>>> the patch doesn't apply. > >>>> > >> Please fix this. > >> > >> ipatests/test_xmlrpc/test_user_plugin.py:1419: > >> [E0602(undefined-variable), > >> TestDeniedBindWithExpiredPrincipal.teardown_class] Undefined > >> variable 'user1') > >> > >> Also, use the version numbers for your changed patches. > >> > > > > > Thanks for the patch. Several issues: > > 1. Use dict.items instead of dict.iteritems, for python3 compatibility > > 2. What is the purpose of TestPrepare class? The 'purge' methods do > not call any ipa commands. > Tracker.make_fixture should be used to make the Tracked resources > clean themselves up when they're out of scope. > > 3. Why reference the resources by hardcoded name if they have a > fixture representation? > > 4. Rewrite {create,delete}_test_group to a fixture. You may want to > use different scope (or not). > > 5. In `def atest_rename_to_invalid_login(self, user):` - use > pytest.skipif decorator and provide a reason if you must, > do not obfuscate method name in order not to run it. > >
>From e2411ccfbabbb2a9e747c547141466ed38331528 Mon Sep 17 00:00:00 2001 From: Filip Skola <fsk...@redhat.com> Date: Fri, 6 Nov 2015 10:57:37 +0100 Subject: [PATCH] Refactor test_user_plugin, use UserTracker for tests --- ipatests/test_xmlrpc/test_user_plugin.py | 2387 ++++++++++----------------- ipatests/test_xmlrpc/tracker/user_plugin.py | 162 +- 2 files changed, 1033 insertions(+), 1516 deletions(-) diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py index 084fb83c42d362204ff4547357226c8f56d217fb..7d299d228aef4881efce5b3f575c95d21a0d41d3 100644 --- a/ipatests/test_xmlrpc/test_user_plugin.py +++ b/ipatests/test_xmlrpc/test_user_plugin.py @@ -2,6 +2,7 @@ # Rob Crittenden <rcrit...@redhat.com> # Pavel Zuna <pz...@redhat.com> # Jason Gerard DeRose <jder...@redhat.com> +# Filip Skola <fsk...@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,42 @@ 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 +from ipapython.version import API_VERSION + +from ipatests.test_xmlrpc.ldaptracker import Tracker +from ipatests.test_xmlrpc.tracker.group_plugin import GroupTracker +from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker -user1 = u'tuser1' -user2 = u'tuser2' 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' 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 +79,867 @@ 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(setattr=(u'uid=%s' % invaliduser2)) + # no exception raised, user is renamed + ) + try: + with raises_exact(errors.ValidationError( + name='login', + error=u'can be at most 32 characters')): + command() + # if the exception isn't raised, rename the user back + # so we can continue running + except AssertionError as ex: + command = user.make_command( + 'user_mod', invaliduser2, **dict(rename=user.uid) + ) + command() + raise ex + + @pytest.mark.skipif(API_VERSION == (u'2.155'), + reason="crash occurs on API version 2.155") + 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(setattr=(u'uid=%s' % invaliduser1)) + # crash + ) + with raises_exact(errors.ValidationError( + name='rename', + error=u'may only include letters, numbers, _, -, . and $')): + 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.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() + + +@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 32 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'tus...@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@b...@notfound.org' + ) + + command = testuser.make_create_command() + with raises_exact(errors.MalformedUserPrincipal( + principal=u'tuser1@b...@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 +1018,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 +1043,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@b...@notfound.org' % user1) - ), - expected=errors.MalformedUserPrincipal( - principal='%s@b...@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 af7d85836a5c514c4e208696398a68ab341e825f..50271558ed2d6f5342b1df76a785d0d541048375 100644 --- a/ipatests/test_xmlrpc/tracker/user_plugin.py +++ b/ipatests/test_xmlrpc/tracker/user_plugin.py @@ -7,7 +7,8 @@ from ipapython.dn import DN from ipatests.util import assert_deepequal, get_group_dn from ipatests.test_xmlrpc import objectclasses -from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_digits, fuzzy_uuid, raises_exact +from ipatests.test_xmlrpc.xmlrpc_test import ( + fuzzy_digits, fuzzy_uuid, raises_exact) from ipatests.test_xmlrpc.tracker.base import Tracker @@ -23,13 +24,15 @@ 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' } 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'} @@ -37,14 +40,18 @@ class UserTracker(Tracker): 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' } + 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 | {u'preserved'} + + primary_keys = {u'uid', u'dn'} def __init__(self, name, givenname, sn, **kwargs): super(UserTracker, self).__init__(default_version=None) @@ -102,11 +109,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( @@ -131,25 +145,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): @@ -160,9 +220,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 @@ -189,13 +248,15 @@ 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 + self.attrs[u'nsaccountlock'] = False + self.attrs[u'preserved'] = False if all: 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) @@ -217,10 +278,35 @@ class UserTracker(Tracker): def check_update(self, result, extra_keys=()): """ Check 'user-mod' command result """ + # small override because stageuser-find returns different + # type of nsaccountlock value than DS, but overall the value + # fits expected 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): @@ -277,6 +363,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: @@ -338,3 +440,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) -- 2.4.6
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code