- Resolves: RHEL-79079 - Failure to get Server monitoring data when NDN cache is disabled. - Resolves: RHEL-87352 - ns-slapd crashed when we add nsslapd-referral - Resolves: RHEL-92054 - Memory leak in roles_cache_create_object_from_entry [rhel-10] - Resolves: RHEL-107001 - ipa-restore fails to restore SELinux contexts, causes ns-slapd AVC denials on /dev/shm after restore. [rhel-10] - Resolves: RHEL-107028 - CWE-284 dirsrv log rotation creates files with world readable permission - Resolves: RHEL-107035 - CWE-532 Created user password hash available to see in audit log - Resolves: RHEL-107037 - CWE-778 Log doesn't show what user gets password changed by administrator
504 lines
18 KiB
Diff
504 lines
18 KiB
Diff
From 10937417415577569bd777aacf7941803e96da21 Mon Sep 17 00:00:00 2001
|
|
From: Lenka Doudova <lryznaro@redhat.com>
|
|
Date: Mon, 20 Jan 2025 14:19:51 +0100
|
|
Subject: [PATCH] Issue 6519 - Add basic dsidm account tests
|
|
|
|
Automating basic dsidm account tests
|
|
|
|
Relates to: https://github.com/389ds/389-ds-base/issues/6519
|
|
|
|
Author: Lenka Doudova
|
|
|
|
Reviewed by: Simon Pichugin
|
|
---
|
|
.../tests/suites/clu/dsidm_account_test.py | 417 +++++++++++++++++-
|
|
src/lib389/lib389/cli_idm/account.py | 6 +-
|
|
2 files changed, 409 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/dirsrvtests/tests/suites/clu/dsidm_account_test.py b/dirsrvtests/tests/suites/clu/dsidm_account_test.py
|
|
index 4b48a11a5..c600e31fd 100644
|
|
--- a/dirsrvtests/tests/suites/clu/dsidm_account_test.py
|
|
+++ b/dirsrvtests/tests/suites/clu/dsidm_account_test.py
|
|
@@ -6,22 +6,19 @@
|
|
# See LICENSE for details.
|
|
# --- END COPYRIGHT BLOCK ---
|
|
#
|
|
+
|
|
import logging
|
|
import os
|
|
import json
|
|
import pytest
|
|
import ldap
|
|
from lib389 import DEFAULT_SUFFIX
|
|
-from lib389.cli_idm.account import (
|
|
- get_dn,
|
|
- lock,
|
|
- unlock,
|
|
- entry_status,
|
|
- subtree_status,
|
|
-)
|
|
+from lib389.cli_idm.account import list, get_dn, lock, unlock, delete, modify, rename, entry_status, \
|
|
+ subtree_status, reset_password, change_password
|
|
+from lib389.cli_idm.user import create
|
|
from lib389.topologies import topology_st
|
|
from lib389.cli_base import FakeArgs
|
|
-from lib389.utils import ds_is_older
|
|
+from lib389.utils import ds_is_older, is_a_dn
|
|
from lib389.idm.user import nsUserAccounts
|
|
from . import check_value_in_log_and_reset
|
|
|
|
@@ -30,13 +27,28 @@ pytestmark = pytest.mark.tier0
|
|
logging.getLogger(__name__).setLevel(logging.DEBUG)
|
|
log = logging.getLogger(__name__)
|
|
|
|
+test_user_name = 'test_user_1000'
|
|
|
|
@pytest.fixture(scope="function")
|
|
def create_test_user(topology_st, request):
|
|
log.info('Create test user')
|
|
users = nsUserAccounts(topology_st.standalone, DEFAULT_SUFFIX)
|
|
- test_user = users.create_test_user()
|
|
- log.info('Created test user: %s', test_user.dn)
|
|
+
|
|
+ if users.exists(test_user_name):
|
|
+ test_user = users.get(test_user_name)
|
|
+ test_user.delete()
|
|
+
|
|
+ properties = FakeArgs()
|
|
+ properties.uid = test_user_name
|
|
+ properties.cn = test_user_name
|
|
+ properties.sn = test_user_name
|
|
+ properties.uidNumber = '1000'
|
|
+ properties.gidNumber = '2000'
|
|
+ properties.homeDirectory = '/home/test_user_1000'
|
|
+ properties.displayName = test_user_name
|
|
+
|
|
+ create(topology_st.standalone, DEFAULT_SUFFIX, topology_st.logcap.log, properties)
|
|
+ test_user = users.get(test_user_name)
|
|
|
|
def fin():
|
|
log.info('Delete test user')
|
|
@@ -74,7 +86,7 @@ def test_dsidm_account_entry_status_with_lock(topology_st, create_test_user):
|
|
|
|
standalone = topology_st.standalone
|
|
users = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
- test_user = users.get('test_user_1000')
|
|
+ test_user = users.get(test_user_name)
|
|
|
|
entry_list = ['Entry DN: {}'.format(test_user.dn),
|
|
'Entry Creation Date',
|
|
@@ -169,8 +181,389 @@ def test_dsidm_account_entry_get_by_dn(topology_st, create_test_user):
|
|
assert json_result['dn'] == user_dn
|
|
|
|
|
|
+@pytest.mark.skipif(ds_is_older("1.4.2"), reason="Not implemented")
|
|
+def test_dsidm_account_delete(topology_st, create_test_user):
|
|
+ """ Test dsidm account delete option
|
|
+
|
|
+ :id: a7960bc2-0282-4a82-8dfb-3af2088ec661
|
|
+ :setup: Standalone
|
|
+ :steps:
|
|
+ 1. Run dsidm account delete on a created account
|
|
+ 2. Check that a message is provided on deletion
|
|
+ 3. Check that the account no longer exists
|
|
+ :expectedresults:
|
|
+ 1. Success
|
|
+ 2. Success
|
|
+ 3. Success
|
|
+ """
|
|
+
|
|
+ standalone = topology_st.standalone
|
|
+ accounts = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
+ test_account = accounts.get(test_user_name)
|
|
+ output = 'Successfully deleted {}'.format(test_account.dn)
|
|
+
|
|
+ args = FakeArgs()
|
|
+ args.dn = test_account.dn
|
|
+
|
|
+ log.info('Test dsidm account delete')
|
|
+ delete(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args, warn=False)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=output)
|
|
+
|
|
+ log.info('Check that the account no longer exists')
|
|
+ assert not test_account.exists()
|
|
+
|
|
+
|
|
+@pytest.mark.skipif(ds_is_older("1.4.2"), reason="Not implemented")
|
|
+def test_dsidm_account_list(topology_st, create_test_user):
|
|
+ """ Test dsidm account list option
|
|
+
|
|
+ :id: 4d173a3e-ee36-4a8b-8d0d-4955c792faca
|
|
+ :setup: Standalone instance
|
|
+ :steps:
|
|
+ 1. Run dsidm account list without json
|
|
+ 2. Check the output content is correct
|
|
+ 3. Run dsidm account list with json
|
|
+ 4. Check the output content is correct
|
|
+ 5. Test full_dn option with list
|
|
+ 6. Delete the account
|
|
+ 7. Check the account is not in the list with json
|
|
+ 8. Check the account is not in the list without json
|
|
+ :expectedresults:
|
|
+ 1. Success
|
|
+ 2. Success
|
|
+ 3. Success
|
|
+ 4. Success
|
|
+ 5. Success
|
|
+ 6. Success
|
|
+ 7. Success
|
|
+ 8. Success
|
|
+ """
|
|
+
|
|
+ standalone = topology_st.standalone
|
|
+ args = FakeArgs()
|
|
+ args.json = False
|
|
+ args.full_dn = False
|
|
+ json_list = ['type',
|
|
+ 'list',
|
|
+ 'items']
|
|
+
|
|
+ log.info('Empty the log file to prevent false data to check about group')
|
|
+ topology_st.logcap.flush()
|
|
+
|
|
+ log.info('Test dsidm account list without json')
|
|
+ list(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=test_user_name)
|
|
+
|
|
+ log.info('Test dsidm account list with json')
|
|
+ args.json = True
|
|
+ list(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ check_value_in_log_and_reset(topology_st, content_list=json_list, check_value=test_user_name)
|
|
+
|
|
+ log.info('Test full_dn option with list')
|
|
+ args.full_dn = True
|
|
+ list(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ result = topology_st.logcap.get_raw_outputs()
|
|
+ json_result = json.loads(result[0])
|
|
+ assert is_a_dn(json_result['items'][0])
|
|
+ args.full_dn = False
|
|
+ topology_st.logcap.flush()
|
|
+
|
|
+ log.info('Delete the account')
|
|
+ accounts = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
+ test_account = accounts.get(test_user_name)
|
|
+ test_account.delete()
|
|
+
|
|
+ log.info('Test empty dsidm account list with json')
|
|
+ list(standalone,DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ check_value_in_log_and_reset(topology_st, content_list=json_list, check_value_not=test_user_name)
|
|
+
|
|
+ log.info('Test empty dsidm account list without json')
|
|
+ args.json = False
|
|
+ list(standalone,DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ check_value_in_log_and_reset(topology_st, check_value_not=test_user_name)
|
|
+
|
|
+
|
|
+@pytest.mark.xfail(reason='DS6515')
|
|
+@pytest.mark.skipif(ds_is_older("1.4.2"), reason="Not implemented")
|
|
+def test_dsidm_account_get_by_dn(topology_st, create_test_user):
|
|
+ """ Test dsidm account get-by-dn option
|
|
+
|
|
+ :id: 07945577-2da0-4fd9-9237-43dd2823f7b8
|
|
+ :setup: Standalone instance
|
|
+ :steps:
|
|
+ 1. Run dsidm account get-by-dn for an account without json
|
|
+ 2. Check the output content is correct
|
|
+ 3. Run dsidm account get-by-dn for an account with json
|
|
+ 4. Check the output content is correct
|
|
+ :expectedresults:
|
|
+ 1. Success
|
|
+ 2. Success
|
|
+ 3. Success
|
|
+ 4. Success
|
|
+ """
|
|
+
|
|
+ standalone = topology_st.standalone
|
|
+ accounts = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
+ test_account = accounts.get(test_user_name)
|
|
+
|
|
+ args = FakeArgs()
|
|
+ args.dn = test_account.dn
|
|
+ args.json = False
|
|
+
|
|
+ account_content = ['dn: {}'.format(test_account.dn),
|
|
+ 'cn: {}'.format(test_account.rdn),
|
|
+ 'displayName: {}'.format(test_user_name),
|
|
+ 'gidNumber: 2000',
|
|
+ 'homeDirectory: /home/{}'.format(test_user_name),
|
|
+ 'objectClass: top',
|
|
+ 'objectClass: nsPerson',
|
|
+ 'objectClass: nsAccount',
|
|
+ 'objectClass: nsOrgPerson',
|
|
+ 'objectClass: posixAccount',
|
|
+ 'uid: {}'.format(test_user_name),
|
|
+ 'uidNumber: 1000']
|
|
+
|
|
+ json_content = ['attrs',
|
|
+ 'objectclass',
|
|
+ 'top',
|
|
+ 'nsPerson',
|
|
+ 'nsAccount',
|
|
+ 'nsOrgPerson',
|
|
+ 'posixAccount',
|
|
+ 'cn',
|
|
+ test_account.rdn,
|
|
+ 'gidnumber',
|
|
+ '2000',
|
|
+ 'homedirectory',
|
|
+ '/home/{}'.format(test_user_name),
|
|
+ 'displayname',
|
|
+ test_user_name,
|
|
+ 'uidnumber',
|
|
+ '1000',
|
|
+ 'creatorsname',
|
|
+ 'cn=directory manager',
|
|
+ 'modifiersname',
|
|
+ 'createtimestamp',
|
|
+ 'modifytimestamp',
|
|
+ 'nsuniqueid',
|
|
+ 'parentid',
|
|
+ 'entryid',
|
|
+ 'entryuuid',
|
|
+ 'dsentrydn',
|
|
+ 'entrydn',
|
|
+ test_account.dn]
|
|
+
|
|
+ log.info('Empty the log file to prevent false data to check about the account')
|
|
+ topology_st.logcap.flush()
|
|
+
|
|
+ log.info('Test dsidm account get-by-dn without json')
|
|
+ get_dn(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ check_value_in_log_and_reset(topology_st, content_list=account_content)
|
|
+
|
|
+ log.info('Test dsidm account get-by-dn with json')
|
|
+ args.json = True
|
|
+ get_dn(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ check_value_in_log_and_reset(topology_st, content_list=json_content)
|
|
+
|
|
+
|
|
+@pytest.mark.skipif(ds_is_older("1.4.2"), reason="Not implemented")
|
|
+def test_dsidm_account_modify_by_dn(topology_st, create_test_user):
|
|
+ """ Test dsidm account modify-by-dn
|
|
+
|
|
+ :id: e7288f8c-f0a8-4d8d-a00f-1b243eb117bc
|
|
+ :setup: Standalone instance
|
|
+ :steps:
|
|
+ 1. Run dsidm account modify-by-dn add description value
|
|
+ 2. Run dsidm account modify-by-dn replace description value
|
|
+ 3. Run dsidm account modify-by-dn delete description value
|
|
+ :expectedresults:
|
|
+ 1. A description value is added
|
|
+ 2. The original description value is replaced and the previous is not present
|
|
+ 3. The replaced description value is deleted
|
|
+ """
|
|
+
|
|
+ standalone = topology_st.standalone
|
|
+ accounts = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
+ test_account = accounts.get(test_user_name)
|
|
+ output = 'Successfully modified {}'.format(test_account.dn)
|
|
+
|
|
+ args = FakeArgs()
|
|
+ args.dn = test_account.dn
|
|
+ args.changes = ['add:description:new_description']
|
|
+
|
|
+ log.info('Test dsidm account modify add')
|
|
+ modify(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args, warn=False)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=output)
|
|
+ assert test_account.present('description', 'new_description')
|
|
+
|
|
+ log.info('Test dsidm account modify replace')
|
|
+ args.changes = ['replace:description:replaced_description']
|
|
+ modify(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args, warn=False)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=output)
|
|
+ assert test_account.present('description', 'replaced_description')
|
|
+ assert not test_account.present('description', 'new_description')
|
|
+
|
|
+ log.info('Test dsidm account modify delete')
|
|
+ args.changes = ['delete:description:replaced_description']
|
|
+ modify(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args, warn=False)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=output)
|
|
+ assert not test_account.present('description', 'replaced_description')
|
|
+
|
|
+
|
|
+@pytest.mark.skipif(ds_is_older("1.4.2"), reason="Not implemented")
|
|
+def test_dsidm_account_rename_by_dn(topology_st, create_test_user):
|
|
+ """ Test dsidm account rename-by-dn option
|
|
+
|
|
+ :id: f4b8e491-35b1-4113-b9c4-e0a80f8985f3
|
|
+ :setup: Standalone instance
|
|
+ :steps:
|
|
+ 1. Run dsidm account rename option on existing account
|
|
+ 2. Check the account does not have another uid attribute with the old rdn
|
|
+ 3. Check the old account is deleted
|
|
+ :expectedresults:
|
|
+ 1. Success
|
|
+ 2. Success
|
|
+ 3. Success
|
|
+ """
|
|
+
|
|
+ standalone = topology_st.standalone
|
|
+ accounts = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
+ test_account = accounts.get(test_user_name)
|
|
+
|
|
+ args = FakeArgs()
|
|
+ args.dn = test_account.dn
|
|
+ args.new_name = 'renamed_account'
|
|
+ args.new_dn = 'uid=renamed_account,ou=people,{}'.format(DEFAULT_SUFFIX)
|
|
+ args.keep_old_rdn = False
|
|
+
|
|
+ log.info('Test dsidm account rename-by-dn')
|
|
+ rename(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ new_account = accounts.get(args.new_name)
|
|
+
|
|
+ try:
|
|
+ output = 'Successfully renamed to {}'.format(new_account.dn)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=output)
|
|
+
|
|
+ log.info('Verify the new account does not have a uid attribute with the old rdn')
|
|
+ assert not new_account.present('uid', test_user_name)
|
|
+ assert new_account.present('displayName', test_user_name)
|
|
+
|
|
+ log.info('Verify the old account does not exist')
|
|
+ assert not test_account.exists()
|
|
+ finally:
|
|
+ log.info('Clean up')
|
|
+ new_account.delete()
|
|
+
|
|
+
|
|
+@pytest.mark.skipif(ds_is_older("1.4.2"), reason="Not implemented")
|
|
+def test_dsidm_account_rename_by_dn_keep_old_rdn(topology_st, create_test_user):
|
|
+ """ Test dsidm account rename-by-dn option with keep-old-rdn
|
|
+
|
|
+ :id: a128bdbb-c0a4-4d9d-9a95-9be2d3780094
|
|
+ :setup: Standalone instance
|
|
+ :steps:
|
|
+ 1. Run dsidm account rename option on existing account
|
|
+ 2. Check the account has another uid attribute with the old rdn
|
|
+ 3. Check the old account is deleted
|
|
+ :expectedresults:
|
|
+ 1. Success
|
|
+ 2. Success
|
|
+ 3. Success
|
|
+ """
|
|
+
|
|
+ standalone = topology_st.standalone
|
|
+ accounts = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
+ test_account = accounts.get(test_user_name)
|
|
+
|
|
+ args = FakeArgs()
|
|
+ args.dn = test_account.dn
|
|
+ args.new_name = 'renamed_account'
|
|
+ args.new_dn = 'uid=renamed_account,ou=people,{}'.format(DEFAULT_SUFFIX)
|
|
+ args.keep_old_rdn = True
|
|
+
|
|
+ log.info('Test dsidm account rename-by-dn')
|
|
+ rename(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ new_account = accounts.get(args.new_name)
|
|
+
|
|
+ try:
|
|
+ output = 'Successfully renamed to {}'.format(new_account.dn)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=output)
|
|
+
|
|
+ log.info('Verify the new account does not have a uid attribute with the old rdn')
|
|
+ assert new_account.present('uid', test_user_name)
|
|
+ assert new_account.present('displayName', test_user_name)
|
|
+
|
|
+ log.info('Verify the old account does not exist')
|
|
+ assert not test_account.exists()
|
|
+ finally:
|
|
+ log.info('Clean up')
|
|
+ new_account.delete()
|
|
+
|
|
+
|
|
+@pytest.mark.skipif(ds_is_older("1.4.2"), reason="Not implemented")
|
|
+def test_dsidm_account_reset_password(topology_st, create_test_user):
|
|
+ """ Test dsidm account reset_password option
|
|
+
|
|
+ :id: 02ffa044-08ae-40c5-9108-b02d0c3b0521
|
|
+ :setup: Standalone instance
|
|
+ :steps:
|
|
+ 1. Run dsidm account reset_password on an existing user
|
|
+ 2. Verify that the user has now userPassword attribute set
|
|
+ :expectedresults:
|
|
+ 1. Success
|
|
+ 2. Success
|
|
+ """
|
|
+
|
|
+ standalone = topology_st.standalone
|
|
+ accounts = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
+ test_account = accounts.get(test_user_name)
|
|
+
|
|
+ args = FakeArgs()
|
|
+ args.dn = test_account.dn
|
|
+ args.new_password = 'newpasswd'
|
|
+ output = 'reset password for {}'.format(test_account.dn)
|
|
+
|
|
+ log.info('Test dsidm account reset_password')
|
|
+ reset_password(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=output)
|
|
+
|
|
+ log.info('Verify the userPassword attribute is set')
|
|
+ assert test_account.present('userPassword')
|
|
+
|
|
+
|
|
+@pytest.mark.skipif(ds_is_older("1.4.2"), reason="Not implemented")
|
|
+def test_dsidm_account_change_password(topology_st, create_test_user):
|
|
+ """ Test dsidm account change_password option
|
|
+
|
|
+ :id: 24c25b8f-df2b-4d43-a88e-47e24bc4ff36
|
|
+ :setup: Standalone instance
|
|
+ :steps:
|
|
+ 1. Run dsidm account change_password on an existing user
|
|
+ 2. Verify that the user has userPassword attribute set
|
|
+ :expectedresults:
|
|
+ 1. Success
|
|
+ 2. Success
|
|
+ """
|
|
+
|
|
+ standalone = topology_st.standalone
|
|
+ accounts = nsUserAccounts(standalone, DEFAULT_SUFFIX)
|
|
+ test_account = accounts.get(test_user_name)
|
|
+
|
|
+ args = FakeArgs()
|
|
+ args.dn = test_account.dn
|
|
+ args.new_password = 'newpasswd'
|
|
+ output = 'changed password for {}'.format(test_account.dn)
|
|
+
|
|
+ log.info('Test dsidm account change_password')
|
|
+ change_password(standalone, DEFAULT_SUFFIX, topology_st.logcap.log, args)
|
|
+ check_value_in_log_and_reset(topology_st, check_value=output)
|
|
+
|
|
+ log.info('Verify the userPassword attribute is set')
|
|
+ assert test_account.present('userPassword')
|
|
+
|
|
+
|
|
if __name__ == '__main__':
|
|
# Run isolated
|
|
# -s for DEBUG mode
|
|
CURRENT_FILE = os.path.realpath(__file__)
|
|
- pytest.main("-s %s" % CURRENT_FILE)
|
|
+ pytest.main("-s {}".format(CURRENT_FILE))
|
|
\ No newline at end of file
|
|
diff --git a/src/lib389/lib389/cli_idm/account.py b/src/lib389/lib389/cli_idm/account.py
|
|
index 8b6f99549..9877c533a 100644
|
|
--- a/src/lib389/lib389/cli_idm/account.py
|
|
+++ b/src/lib389/lib389/cli_idm/account.py
|
|
@@ -12,10 +12,12 @@ import ldap
|
|
import math
|
|
from datetime import datetime
|
|
from lib389.idm.account import Account, Accounts, AccountState
|
|
-from lib389.cli_base import (
|
|
- _generic_get_dn,
|
|
+from lib389.cli_idm import (
|
|
_generic_list,
|
|
_generic_delete,
|
|
+ _generic_get_dn
|
|
+)
|
|
+from lib389.cli_base import (
|
|
_generic_modify_dn,
|
|
_get_arg,
|
|
_get_dn_arg,
|
|
--
|
|
2.49.0
|
|
|