>From 881f5f980e820f178850307c0f64e3eb50cbc01e 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 | 2794 +++++++++++++----------------- 1 file changed, 1182 insertions(+), 1612 deletions(-) diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py index 3d7b5e6ba14e3ccb144575f52e4e503e6638037d..ede6a339a519dfd40fd7507ee644f1cab0d4503c 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 functools import datetime import ldap @@ -33,40 +35,37 @@ from ipatests.test_xmlrpc import objectclasses from ipatests.util import ( assert_equal, assert_not_equal, raises, assert_deepequal) from xmlrpc_test import ( - XMLRPC_test, Declarative, fuzzy_digits, fuzzy_uuid, fuzzy_password, + XMLRPC_test, fuzzy_digits, fuzzy_uuid, fuzzy_password, fuzzy_string, fuzzy_dergeneralizedtime, add_sid, add_oc, raises_exact) from ipapython.dn import DN from ipatests.test_xmlrpc.ldaptracker import Tracker -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) @@ -78,1581 +77,6 @@ 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$') -def get_user_result(uid, givenname, sn, operation='show', omit=[], - **overrides): - """Get a user result for a user-{add,mod,find,show} command - - This gives the result as from a user_add(uid, givenname=givenname, sn=sn); - modifications to that can be specified in ``omit`` and ``overrides``. - - The ``operation`` can be one of: - - add - - show - - show-all ((show with the --all flag) - - find - - mod - - Attributes named in ``omit`` are removed from the result; any additional - or non-default values can be specified in ``overrides``. - """ - # sn can be None; this should only be used from `get_admin_result` - cn = overrides.get('cn', ['%s %s' % (givenname, sn or '')]) - cn[0] = cn[0].strip() - result = add_sid(dict( - homedirectory=[u'/home/%s' % uid], - loginshell=[u'/bin/sh'], - uid=[uid], - uidnumber=[fuzzy_digits], - gidnumber=[fuzzy_digits], - mail=[u'%s@%s' % (uid, api.env.domain)], - has_keytab=False, - has_password=False, - )) - if sn: - result['sn'] = [sn] - if givenname: - result['givenname'] = [givenname] - if operation in ('add', 'show', 'show-all', 'find'): - result.update( - dn=get_user_dn(uid), - ) - if operation in ('add', 'show-all'): - result.update( - cn=cn, - displayname=cn, - gecos=cn, - initials=[givenname[0] + (sn or '')[:1]], - ipauniqueid=[fuzzy_uuid], - mepmanagedentry=[get_group_dn(uid)], - objectclass=add_oc(objectclasses.user, u'ipantuserattrs'), - krbprincipalname=[u'%s@%s' % (uid, api.env.realm)], - ) - if operation in ('show', 'show-all', 'find', 'mod'): - result.update( - nsaccountlock=False, - ) - if operation in ('add', 'show', 'show-all', 'mod'): - result.update( - memberof_group=[u'ipausers'], - ) - for key in omit: - del result[key] - result.update(overrides) - return result - - -def get_admin_result(operation='show', **overrides): - """Give the result for the default admin user - - Any additional or non-default values can be given in ``overrides``. - """ - result = get_user_result(u'admin', None, u'Administrator', operation, - omit=['mail'], - has_keytab=True, - has_password=True, - loginshell=[u'/bin/bash'], - **overrides) - return result - - -def get_user_dn(uid): - return DN(('uid', uid), api.env.container_user, api.env.basedn) - - -def get_group_dn(cn): - return DN(('cn', cn), api.env.container_group, api.env.basedn) - - -def upg_check(response): - """Check that the user was assigned to the corresponding private group.""" - assert_equal(response['result']['uidnumber'], - response['result']['gidnumber']) - return True - - -def not_upg_check(response): - """ - Check that the user was not assigned to the corresponding - private group. - """ - - assert_not_equal(response['result']['uidnumber'], - response['result']['gidnumber']) - return True - - -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') - ), - ), - - ] - - -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) - - class UserTracker(Tracker): """ Class for host plugin like tests """ @@ -1665,13 +89,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'} @@ -1679,14 +105,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) @@ -1744,11 +174,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( @@ -1771,27 +208,67 @@ class UserTracker(Tracker): loginshell=[u'/bin/sh'], has_keytab=False, has_password=False, - mepmanagedentry=[get_group_dn(self.uid)], + mepmanagedentry=[self.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]] 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.iteritems(): + if value is None: + del self.attrs[key] + else: + self.attrs[key] = [value] + for key, value in expected_updates.iteritems(): + if value is None: + 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): @@ -1802,9 +279,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 @@ -1831,13 +307,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) @@ -1859,10 +337,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): @@ -1919,6 +422,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: @@ -1980,3 +499,1054 @@ 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': self.get_group_dn(admin_group), + 'member_user': admin_group_expected, + 'gidnumber': [fuzzy_digits], + 'cn': [admin_group], + 'description': [u'Account administrators group'], + }, + ), result) + + def get_group_dn(self, cn): + """ Get group DN by CN """ + return DN(('cn', cn), api.env.container_group, api.env.basedn) + + def get_user_dn(self, uid=None): + """ Get user DN by uid """ + if not uid: + uid = self.uid + + return DN(('uid', uid), api.env.container_user, api.env.basedn) + + +@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): + """ 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=[group1], + objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs') + ) + return tracker + + +@pytest.fixture(scope='class') +def user_npg2(request): + """ 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=[group1], + objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs') + ) + return tracker + + +def create_test_group(): + """ Create group for tests using users with an own group """ + user = UserTracker(name=u'nonuser', givenname=u'Nuser', sn=u'Nuser1') + result = user.run_command( + 'group_add', group1, **dict(description=u'Test desc') + ) + assert_deepequal(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=user.get_group_dn(group1)) + ), result) + user.ensure_missing() + + +def delete_test_group(): + """ Delete the group for tests using users with an own group """ + tracker = Tracker() + tracker.run_command('group_del', group1) + + +class TestPrepare(XMLRPC_test): + def test_ensure_defaults_are_set(self): + """ Ensure expected default values are set prior running the tests """ + tracker = Tracker() + mods = [{u'ipadefaultloginshell': u'/bin/sh'}, + {u'ipahomesrootdir': u'/home'}, + {u'ipadefaultprimarygroup': u'ipausers'}] + + for mod in mods: + try: + tracker.run_command('config_mod', **mod) + except errors.EmptyModlist as ex: + pass + + def test_purge_testusers(self): + """ Remove test users after previous test runs """ + testusers = [u'user1', u'user2', u'ruser1', u'tuser1', u'npguser1', + u'npguser2', u'renameduser', u'renamed_npg2', u'admin2', + invaliduser1, invaliduser2] + tracker = Tracker() + + for user in testusers: + try: + tracker.make_command('user_del', user) + except errors.NotFound as ex: + pass + + def test_purge_testgroups(self): + """ Remove test groups after previous test runs """ + testgroups = [group1] + tracker = Tracker() + + for group in testgroups: + try: + tracker.make_command('group_del', group) + except errors.NotFound as ex: + pass + + +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() + + +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() + + +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) + + +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)) + + +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=u'user1') + ) + command() + raise ex + + # FIXME skip this case as it crashes the server and we can't go on + def atest_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() + + +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): + """ Create a managed group and then try to create user + with the same name the group has """ + create_test_group() + command = user.make_command( + 'user_add', group1, **dict(givenname=u'Test', sn=u'User1') + ) + with raises_exact(errors.ManagedGroupExistsError(group=group1)): + command() + + delete_test_group() + + +class TestUserWithGroup(XMLRPC_test): + def test_change_default_user_group(self): + """ Change default group for TestUserWithGroup class of tests """ + create_test_group() + tracker = Tracker() + tracker.run_command( + 'config_mod', **{u'ipadefaultprimarygroup': u'group1'} + ) + + 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'} + ) + delete_test_group() + + +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 + + +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) + + +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() + + +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() + + +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) + + +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): + cls.failsafe_del(api.Object.user, user1) + super(test_denied_bind_with_expired_principal, 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(user.get_user_dn()), 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(user.get_user_dn()), 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(user.get_user_dn()), 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 + + This gives the result as from a user_add(uid, givenname=givenname, sn=sn); + modifications to that can be specified in ``omit`` and ``overrides``. + + The ``operation`` can be one of: + - add + - show + - show-all ((show with the --all flag) + - find + - mod + + Attributes named in ``omit`` are removed from the result; any additional + or non-default values can be specified in ``overrides``. + """ + # sn can be None; this should only be used from `get_admin_result` + cn = overrides.get('cn', ['%s %s' % (givenname, sn or '')]) + cn[0] = cn[0].strip() + result = add_sid(dict( + homedirectory=[u'/home/%s' % uid], + loginshell=[u'/bin/sh'], + uid=[uid], + uidnumber=[fuzzy_digits], + gidnumber=[fuzzy_digits], + mail=[u'%s@%s' % (uid, api.env.domain)], + has_keytab=False, + has_password=False, + )) + if sn: + result['sn'] = [sn] + if givenname: + result['givenname'] = [givenname] + if operation in ('add', 'show', 'show-all', 'find'): + result.update( + dn=get_user_dn(uid), + ) + if operation in ('add', 'show-all'): + result.update( + cn=cn, + displayname=cn, + gecos=cn, + initials=[givenname[0] + (sn or '')[:1]], + ipauniqueid=[fuzzy_uuid], + mepmanagedentry=[get_group_dn(uid)], + objectclass=add_oc(objectclasses.user, u'ipantuserattrs'), + krbprincipalname=[u'%s@%s' % (uid, api.env.realm)], + ) + if operation in ('show', 'show-all', 'find', 'mod'): + result.update( + nsaccountlock=False, + ) + if operation in ('add', 'show', 'show-all', 'mod'): + result.update( + memberof_group=[u'ipausers'], + ) + for key in omit: + del result[key] + result.update(overrides) + return result + + +def get_admin_result(operation='show', **overrides): + """Give the result for the default admin user + + Any additional or non-default values can be given in ``overrides``. + """ + result = get_user_result(u'admin', None, u'Administrator', operation, + omit=['mail'], + has_keytab=True, + has_password=True, + loginshell=[u'/bin/bash'], + **overrides) + return result + + +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) + + +def upg_check(response): + """Check that the user was assigned to the corresponding private group.""" + assert_equal(response['result']['uidnumber'], + response['result']['gidnumber']) + return True + + +def not_upg_check(response): + """ + Check that the user was not assigned to the corresponding + private group. + """ + + assert_not_equal(response['result']['uidnumber'], + response['result']['gidnumber']) + return True -- 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