389-ds-base/0036-Issue-6519-Add-basic-dsidm-account-tests.patch
Viktor Ashirov 23917c9198 - Resolves: RHEL-73032 - segfault - error 4 in libpthread-2.28.so
- 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
2025-08-05 19:36:51 +02:00

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