From aa5726420eb2d6f89ca8cb133e2f682546a185a9 Mon Sep 17 00:00:00 2001 From: Dan Lavu Date: Sat, 13 Jul 2024 01:13:33 -0400 Subject: [PATCH] cherrypick to sssd-2-8 - tests: housekeeping - schema housekeeping, the following is looked at and may have been done: * fixed typos and standardized formatting * renamed test cases to improve the clarity of what the test does * improved docstring language, setup, steps and expected results * synced code with the docstring order * removed necessary configuration relevant to the test * added pytest.mark.importance to test cases * added error messages to assertions Notable changes: * added integration marker * moved schema tests to cache * renamed schema test names (cherry picked from commit fcda45b05f1cc95c5735f770d1d860b34351a027) --- src/tests/system/pytest.ini | 9 +- src/tests/system/tests/test_cache.py | 320 ++++++++++++++++++++++++++ src/tests/system/tests/test_schema.py | 48 ++++ 3 files changed, 375 insertions(+), 2 deletions(-) create mode 100644 src/tests/system/tests/test_cache.py create mode 100644 src/tests/system/tests/test_schema.py diff --git a/src/tests/system/pytest.ini b/src/tests/system/pytest.ini index cdf3879cac9..63d1a0cc8a2 100644 --- a/src/tests/system/pytest.ini +++ b/src/tests/system/pytest.ini @@ -3,7 +3,12 @@ addopts = --strict-markers testpaths = tests markers = - slow: marks tests as slow (deselect with '-m "not slow"') + authentication: cache: - contains_workaround_for(gh=...,bz=...): test requires workaround for an existing bug + config: + contains_workaround_for(gh=...,bz=...): + identity: + integration: + slow: + tools: ticket_tools = bz,gh,jira diff --git a/src/tests/system/tests/test_cache.py b/src/tests/system/tests/test_cache.py new file mode 100644 index 00000000000..00f5a52f21a --- /dev/null +++ b/src/tests/system/tests/test_cache.py @@ -0,0 +1,320 @@ +""" +SSSD Cache Tests. + +Tests pertaining SSSD caches, the following types are tested and some will be in other python files. + +* Local cache (LDB) +* Negative cache (ncache) +* In-memory cache (memcache): test_memcache.py + +:requirement: Cache +""" + +from __future__ import annotations + +import time + +import pytest +from sssd_test_framework.roles.client import Client +from sssd_test_framework.roles.generic import GenericProvider +from sssd_test_framework.roles.ldap import LDAP +from sssd_test_framework.topology import KnownTopology, KnownTopologyGroup + + +@pytest.mark.integration +@pytest.mark.importance("low") +@pytest.mark.topology(KnownTopologyGroup.AnyProvider) +def test_cache__entries_are_refreshed_as_configured(client: Client, provider: GenericProvider): + """ + :title: Ensuring LDB cache refreshes at configured intervals + :setup: + 1. Create user + 2. Create group + 3. Create netgroup + 4. Configure SSSD and set 'entry_cache_timeout to 1' and 'refresh_expired_interval to 2' + 5. Restart SSSD + 6. Lookup user, group and netgroup + :steps: + 1. Search for objects lastUpdate and dataExpireTimestamp in ldb database + 2. Wait 5 seconds and repeat search + :expectedresults: + 1. The 'dataExpireTimestamp' value equals the 'lastUpdate + entry_cache_timeout' value + 2. Objects 'lastUpdate' timestamp value has been refreshed + :customerscenario: False + """ + user = provider.user("test_user").add() + provider.group("test_group").add().add_member(user) + provider.netgroup("test_netgroup").add().add_member(user=user) + + domain = client.sssd.default_domain + entry_cache_timeout = 1 + refresh_expired_interval = 2 + + client.sssd.domain["entry_cache_timeout"] = str(entry_cache_timeout) + client.sssd.domain["refresh_expired_interval"] = str(refresh_expired_interval) + + client.sssd.restart() + client.tools.getent.passwd(f"test_user@{domain}") + client.tools.getent.group(f"test_group@{domain}") + client.tools.getent.netgroup(f"test_netgroup@{domain}") + + ldb_cache = f"/var/lib/sss/db/cache_{domain}.ldb" + ldb_suffix = f"cn={domain},cn=sysdb" + + last_update: list[int] = [] + expire_time: list[int] = [] + + for i in [f"test_user@{domain}", f"test_group@{domain}", "test_netgroup"]: + result = client.ldb.search(ldb_cache, ldb_suffix, filter=f"name={i}") + for k, v in result.items(): + for y in v.items(): + if y[0] == "lastUpdate": + last_update = last_update + [(int(y[1][0]))] + if y[0] == "dataExpireTimestamp": + expire_time = expire_time + [(int(y[1][0]))] + + for m, n in enumerate(last_update): + assert ( + last_update[m] + entry_cache_timeout == expire_time[m] + ), f"{expire_time[m]} != {last_update[m]} + {entry_cache_timeout}" + + time.sleep(5) + + for s, t in enumerate([f"test_user@{domain}", f"test_group@{domain}", "test_netgroup"]): + result = client.ldb.search(ldb_cache, ldb_suffix, filter=f"name={t}") + for k, v in result.items(): + for y in v.items(): + if y[0] == "lastUpdate": + assert last_update[s] <= (int(y[1][0])), f"{s} lastUpdate value is greater than expected!" + + +@pytest.mark.integration +@pytest.mark.importance("low") +@pytest.mark.topology(KnownTopologyGroup.AnyProvider) +def test_cache__writes_to_both_database_files(client: Client, provider: GenericProvider): + """ + :title: Search for user in the following ldb databases, cache_*.ldb and timestamp_*.ldb + :setup: + 1. Create user + 2. Start SSSD + :steps: + 1. Lookup user + 2. Check cache + 3. Lookup user in cache ldb database + 4. Lookup user in timestamp ldb database + :expectedresults: + 1. User is found + 2. Cache file exists + 3. User found + 4. User found + :customerscenario: False + """ + provider.user("user1").add() + client.sssd.start() + client.tools.getent.passwd("user1") + cache = "/var/lib/sss/db/cache_test.ldb" + timestamps = "/var/lib/sss/db/timestamps_test.ldb" + assert client.fs.exists(timestamps), f"Timestamp file '{timestamps}' does not exist" + + ldb1 = client.ldb.search(cache, "name=user1@test,cn=users,cn=test,cn=sysdb") + ldb2 = client.ldb.search(timestamps, "name=user1@test,cn=users,cn=test,cn=sysdb") + assert ldb1 != {}, f"ldbsearch failed to find user1 in {cache}" + assert ldb2 != {}, f"ldbsearch failed to find user1 in {timestamps}" + + +@pytest.mark.integration +@pytest.mark.importance("low") +@pytest.mark.topology(KnownTopologyGroup.AnyProvider) +def test_cache__writes_to_both_database_files_when_using_fully_qualified_names( + client: Client, provider: GenericProvider +): + """ + :title: Search for user using fully qualified name in the following ldb databases, cache_*.ldb and timestamp_*.ldb + :setup: + 1. Create user + 2. Start SSSD + :steps: + 1. Lookup user + 2. Lookup user in cache ldb database + 3. Lookup user in timestamp ldb database + :expectedresults: + 1. User found + 2. User found + 3. User found + :customerscenario: False + """ + provider.user("user1").add() + client.sssd.domain["use_fully_qualified_names"] = "True" + client.sssd.start() + client.tools.getent.passwd("user1@test") + + cache = "/var/lib/sss/db/cache_test.ldb" + timestamps = "/var/lib/sss/db/timestamps_test.ldb" + user_basedn = "name=user1@test,cn=users,cn=test,cn=sysdb" + ldb1 = client.ldb.search(cache, user_basedn) + ldb2 = client.ldb.search(timestamps, user_basedn) + + assert ldb1 != {}, f"ldbsearch failed to find user1@test in {cache}" + assert ldb2 != {}, f"ldbsearch failed to find user1@test in {timestamps}" + + +@pytest.mark.integration +@pytest.mark.importance("low") +@pytest.mark.topology(KnownTopologyGroup.AnyProvider) +def test_cache__user_entries_contains_latest_changes_when_modified_and_deleted( + client: Client, provider: GenericProvider +): + """ + :title: Check ldb database for latest user changes when modified and deleted + :setup: + 1. Add users 'user-modify' and 'user-delete' + 2. Start SSSD + 3. Lookup users + :steps: + 1. Login as users + 2. Modify 'user-modify' shell and delete 'user-delete' and clear cache + 3. Login as users + 4. Lookup user 'user-delete' + 5. Lookup user 'user-modify' + :expectedresults: + 1. Users logged in + 2. User 'user-modify' is modified and user 'user-delete' is deleted + 3. User 'user-modify' logged in + 4. User 'user-delete' is not found + 5. User 'user-modify' is found and shell was updated + :customerscenario: False + """ + provider.user("user-modify").add(shell="/bin/bash") + provider.user("user-delete").add(shell="/bin/bash") + client.sssd.start() + client.tools.getent.passwd("user-modify") + client.tools.getent.passwd("user-delete") + + assert client.auth.ssh.password("user-modify", "Secret123"), "Login failed!" + assert client.auth.ssh.password("user-delete", "Secret123"), "Login failed!" + + provider.user("user-delete").delete() + provider.user("user-modify").modify(shell="/bin/sh") + + client.sssctl.cache_expire(everything=True) + + assert client.auth.ssh.password("user-modify", "Secret123"), "Login failed!" + assert not client.auth.ssh.password("user-delete", "Secret123"), "Login successful!" + + result = client.tools.getent.passwd("user-modify") + assert result is not None, "User not found!" + assert result.shell == "/bin/sh", "User shell did not update!" + + +@pytest.mark.integration +@pytest.mark.importance("low") +@pytest.mark.topology(KnownTopologyGroup.AnyProvider) +def test_cache__extra_attributes_are_stored(client: Client, provider: GenericProvider): + """ + :title: Extra attributes are cached + :setup: + 1. Create user "user1" + 2. Edit SSSD configuration and set "ldap_user_extra_attrs = + description:gecos, userID:uidNumber, shell:loginShell, groupID:gidNumber" and + "ldap_id_mapping = false" + 3. Start SSSD + :steps: + 1. Lookup user + 2. Lookup user in cache + :expectedresults: + 1. User is found + 2. User is found and cache contains correct attributes and values + :customerscenario: True + """ + provider.user("user1").add(gid=111111, uid=100110, gecos="gecos user1", shell="/bin/sh", home="/home/user1") + client.sssd.domain["ldap_user_extra_attrs"] = ( + "description:gecos, userID:uidNumber, shell:loginShell, groupID:gidNumber" + ) + client.sssd.domain["ldap_id_mapping"] = "false" + client.sssd.start() + + result = client.tools.getent.passwd("user1") + assert result is not None, "User not found!" + + search = client.ldb.search( + f"/var/lib/sss/db/cache_{client.sssd.default_domain}.ldb", f"cn=users,cn={client.sssd.default_domain},cn=sysdb" + ) + + user_dict = search["name=user1@test,cn=users,cn=test,cn=sysdb"] + assert user_dict["description"] == ["gecos user1"], "attribute 'description' was not correct" + assert user_dict["shell"] == ["/bin/sh"], "attribute 'shell' was not correct" + assert user_dict["userID"] == ["100110"], "attribute 'userID' was not correct" + assert user_dict["groupID"] == ["111111"], "attribute 'groupID' was not correct" + + +@pytest.mark.integration +@pytest.mark.importance("low") +@pytest.mark.topology(KnownTopologyGroup.AnyProvider) +def test_cache__extra_attributes_with_empty_values_are_ignored(client: Client, provider: GenericProvider): + """ + :title: When extra attribute of user is added but not assigned, it is neither cached nor displayed + :setup: + 1. Create user "user1" + 2. Configure SSSD with "ldap_user_extra_attr = number:telephonenumber" + 3. Start SSSD + :steps: + 1. Lookup user + 2. Lookup user in cache + :expectedresults: + 1. User is found + 2. User is found and does not have the extra numbers attribute + :customerscenario: False + """ + provider.user("user1").add() + client.sssd.domain["ldap_user_extra_attrs"] = "number:telephonenumber" + client.sssd.start() + + result = client.tools.getent.passwd("user1") + assert result is not None, "User is not found!" + + search = client.ldb.search( + f"/var/lib/sss/db/cache_{client.sssd.default_domain}.ldb", f"cn=users,cn={client.sssd.default_domain},cn=sysdb" + ) + assert search != {}, "User not found!" + + search = client.ldb.search(f"/var/lib/sss/db/cache_{client.sssd.default_domain}.ldb", "number=*") + assert search == {} + + +@pytest.mark.integration +@pytest.mark.importance("low") +@pytest.mark.topology(KnownTopology.LDAP) +def test_cache__both_ldap_user_email_and_extra_attribute_email_are_stored(client: Client, ldap: LDAP): + """ + :title: Setting ldap_user_email and email using extra attributes are cached + :setup: + 1. Create user "user1" with gecos and mail attributes` + 2. Configure SSSD with "ldap_user_extra_attrs = email:mail, description:gecos" and + "ldap_user_email = mail" + 3. Start SSSD + :steps: + 1. Lookup user + 2. Lookup user in cache + :expectedresults: + 1. User is found + 2. User is found with description, mail and email attributes + :customerscenario: False + """ + ldap.user("user1").add(gecos="gecos1", mail="user1@example.test") + + client.sssd.domain["ldap_user_email"] = "mail" + client.sssd.domain["ldap_user_extra_attrs"] = "email:mail, description:gecos" + client.sssd.start() + + result = client.tools.getent.passwd("user1") + assert result is not None, "User is not found" + assert result.name == "user1", "User has wrong name" + + search = client.ldb.search( + f"/var/lib/sss/db/cache_{client.sssd.default_domain}.ldb", f"cn=users,cn={client.sssd.default_domain},cn=sysdb" + ) + + user_dict = search["name=user1@test,cn=users,cn=test,cn=sysdb"] + assert user_dict["description"] == ["gecos1"], "attribute 'description' was not correct" + assert user_dict["mail"] == ["user1@example.test"], "attribute 'mail' was not correct" + assert user_dict["email"] == ["user1@example.test"], "attribute 'email' was not correct" diff --git a/src/tests/system/tests/test_schema.py b/src/tests/system/tests/test_schema.py new file mode 100644 index 00000000000..9c486df2d4f --- /dev/null +++ b/src/tests/system/tests/test_schema.py @@ -0,0 +1,48 @@ +""" +SSSD Schema Tests. + +Tests related to directory schemas, formal definitions of LDAP objectClasses and attributes. + +These tests are generic topology and will run against AD, Samba, IPA and LDAP. +Specific topologies test may reside in their corresponding test file. + +:requirement: ldap_extra_attrs +""" + +from __future__ import annotations + +import pytest +from sssd_test_framework.roles.client import Client +from sssd_test_framework.roles.generic import GenericProvider +from sssd_test_framework.topology import KnownTopologyGroup + + +@pytest.mark.importance("high") +@pytest.mark.ticket(gh=4153, bz=1362023) +@pytest.mark.topology(KnownTopologyGroup.AnyProvider) +@pytest.mark.parametrize("attrs", ["mail, firstname:givenname, lastname:sn", "given_email:mail"]) +def test_schema__user_extra_attributes_are_populated(client: Client, provider: GenericProvider, attrs: str): + """ + :title: SSSD starts correctly when ldap_extra_attrs is configured + :setup: + 1. Create user "user1" + 2. Configure SSSD with "ldap_user_extra_attrs = attribute:value" + :steps: + 1. Start SSSD + 2. Lookup user + :expectedresults: + 1. SSSD starts with no errors + 2. User found and name matches + :customerscenario: False + """ + provider.user("user1").add() + client.sssd.domain["ldap_user_extra_attrs"] = attrs + + try: + client.sssd.start() + except Exception as e: + pytest.fail(f"Exception shouldn't be raised but we got {type(e)}: str(e)") + + result = client.tools.getent.passwd("user1") + assert result is not None, "User not found!" + assert result.name == "user1", f"User 'user1' name is not the expected value `{result.name}`!"