- Resolves: RHEL-155027 Adding a group with 32Bit Idrange fails - Resolves: RHEL-153145 IdM password policy Min lifetime is not enforced when high minlife is set - Resolves: RHEL-166864 Include latest fixes in python3-ipatests package Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
422 lines
15 KiB
Diff
422 lines
15 KiB
Diff
From b05586c2a6a81c7121dd40f8d627cd8a2c5908d8 Mon Sep 17 00:00:00 2001
|
|
From: Sudhir Menon <sumenon@redhat.com>
|
|
Date: Wed, 18 Mar 2026 16:36:06 +0530
|
|
Subject: [PATCH] ipatests: Additional tests for 32BitIdranges
|
|
|
|
Below tests are added
|
|
|
|
1. Create ipauser with 32bit id.
|
|
2. Create ipagroup with 32Bit id.
|
|
3. Create ipauser with 32Bit groupid range.
|
|
4. Test ssh login with 32Bit id user.
|
|
5. Test that ipauser with 32Bit is replicated.
|
|
6. Test that 32Bit idrange is created in IPA-AD trust enviornment.
|
|
|
|
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
Reviewed-By: David Hanina <dhanina@redhat.com>
|
|
---
|
|
.../test_integration/test_32bit_idranges.py | 333 +++++++++++++++---
|
|
1 file changed, 284 insertions(+), 49 deletions(-)
|
|
|
|
diff --git a/ipatests/test_integration/test_32bit_idranges.py b/ipatests/test_integration/test_32bit_idranges.py
|
|
index a928628d399d3a94901f0220c3af3e97c5115ffe..9b91fc6182f324060a0e38dfaa5ec222f3ef0350 100644
|
|
--- a/ipatests/test_integration/test_32bit_idranges.py
|
|
+++ b/ipatests/test_integration/test_32bit_idranges.py
|
|
@@ -4,17 +4,85 @@
|
|
|
|
from __future__ import absolute_import
|
|
|
|
+import re
|
|
+
|
|
from ipatests.pytest_ipa.integration import tasks
|
|
from ipatests.test_integration.base import IntegrationTest
|
|
from ipatests.test_integration.test_trust import BaseTestTrust
|
|
|
|
+# The tests focus on 32-bit UID/GID creation and replication,
|
|
+# SID behavior is not covered in the tests.
|
|
+
|
|
+# Range with First Posix ID >= 2^31 is considered a 32-bit range.
|
|
+IDRANGE_32BIT_NAME = "{realm}_upper_32bit_range"
|
|
+IDRANGE_32BIT_BASE_ID = 1 << 31 # 2147483648
|
|
+
|
|
+
|
|
+def _32bit_idrange_exists(master):
|
|
+ """
|
|
+ Return True if an ipa-local range with base ID >= 2^31 already exists.
|
|
+ """
|
|
+ result = master.run_command(
|
|
+ ["ipa", "idrange-find", "--type", "ipa-local"]
|
|
+ )
|
|
+ # Parse all "First Posix ID of the range: in the output'
|
|
+ for match in re.finditer(
|
|
+ r"First Posix ID of the range:\s*(\d+)",
|
|
+ result.stdout_text
|
|
+ ):
|
|
+ if int(match.group(1)) >= IDRANGE_32BIT_BASE_ID:
|
|
+ return True
|
|
+ return False
|
|
+
|
|
+
|
|
+def _add_32bit_idrange_if_missing(master):
|
|
+ """
|
|
+ Create the 32-bit ID range only if it does not already exist.
|
|
+ Returns True if the range was added, False if it already existed.
|
|
+ """
|
|
+ if _32bit_idrange_exists(master):
|
|
+ return False
|
|
+ idrange = IDRANGE_32BIT_NAME.format(realm=master.domain.realm)
|
|
+ id_length = 10000
|
|
+ rid_base = 300_000_000
|
|
+ secondary_rid_base = 500_000_000
|
|
+ master.run_command(
|
|
+ [
|
|
+ "ipa",
|
|
+ "idrange-add",
|
|
+ idrange,
|
|
+ "--base-id", str(IDRANGE_32BIT_BASE_ID),
|
|
+ "--range-size", str(id_length),
|
|
+ "--rid-base", str(rid_base),
|
|
+ "--secondary-rid-base", str(secondary_rid_base),
|
|
+ "--type=ipa-local"
|
|
+ ]
|
|
+ )
|
|
+ # Restart dirsrv instance after the new idrange is added.
|
|
+ tasks.restart_ipa_server(master)
|
|
+ # Clear SSSD cache
|
|
+ tasks.clear_sssd_cache(master)
|
|
+ return True
|
|
+
|
|
|
|
class Test32BitIdRanges(IntegrationTest):
|
|
topology = "line"
|
|
+ num_replicas = 1
|
|
+ num_clients = 1
|
|
+ # Counter for 32-bit UID/GID allocation; reset in install() so each
|
|
+ # test run starts from 0 (install/uninstall gives a fresh environment).
|
|
+ id_counter = 0
|
|
+
|
|
+ def get_next_32bit_id(self):
|
|
+ """
|
|
+ Generate unique 32-bit IDs for testing
|
|
+ """
|
|
+ self.id_counter += 1
|
|
+ return IDRANGE_32BIT_BASE_ID + self.__class__.id_counter
|
|
|
|
def test_remove_subid_range(self):
|
|
"""
|
|
- Test that allocating subid will fail after disabling global option
|
|
+ Test that allocating subids will fail after disabling the attribute
|
|
"""
|
|
master = self.master
|
|
tasks.kinit_admin(master)
|
|
@@ -23,19 +91,28 @@ class Test32BitIdRanges(IntegrationTest):
|
|
master.run_command(
|
|
["ipa", "config-mod", "--addattr", "ipaconfigstring=SubID:Disable"]
|
|
)
|
|
- master.run_command(["ipa", "idrange-del", idrange])
|
|
+ master.run_command(
|
|
+ ["ipa", "idrange-del", idrange]
|
|
+ )
|
|
+ master.run_command(["systemctl", "restart", "sssd"])
|
|
|
|
tasks.user_add(master, 'subiduser')
|
|
- result = master.run_command(
|
|
- ["ipa", "subid-generate", "--owner", "subiduser"], raiseonerr=False
|
|
- )
|
|
- assert result.returncode > 0
|
|
- assert "Support for subordinate IDs is disabled" in result.stderr_text
|
|
- tasks.user_del(master, 'subiduser')
|
|
+ try:
|
|
+ result = master.run_command(
|
|
+ ["ipa", "subid-generate", "--owner", "subiduser"],
|
|
+ raiseonerr=False
|
|
+ )
|
|
+ assert result.returncode > 0
|
|
+ assert "Support for subordinate IDs is disabled" in \
|
|
+ result.stderr_text
|
|
+ finally:
|
|
+ # Cleanup: Remove test user
|
|
+ tasks.user_del(master, 'subiduser')
|
|
|
|
def test_invoke_upgrader(self):
|
|
- """Test that ipa-server-upgrade does not add subid ranges back"""
|
|
-
|
|
+ """
|
|
+ Test that ipa-server-upgrade does not add subid ranges back.
|
|
+ """
|
|
master = self.master
|
|
master.run_command(['ipa-server-upgrade'], raiseonerr=True)
|
|
idrange = f"{master.domain.realm}_subid_range"
|
|
@@ -58,69 +135,227 @@ class Test32BitIdRanges(IntegrationTest):
|
|
assert "dnatype: " not in output
|
|
|
|
def test_create_user_with_32bit_id(self):
|
|
- """Test that ID range above 2^31 can be used to assign IDs
|
|
- to users and groups. Also check that SIDs generated properly.
|
|
"""
|
|
+ Test checks that 32Bit idrange is assigned to the user
|
|
+ and getent passwd <username> returns the output.
|
|
+ """
|
|
+ master = self.master
|
|
+ _add_32bit_idrange_if_missing(master)
|
|
+
|
|
+ uid = self.get_next_32bit_id()
|
|
+ gid = self.get_next_32bit_id()
|
|
+
|
|
+ tasks.clear_sssd_cache(master)
|
|
+ username = "user"
|
|
+ tasks.create_active_user(
|
|
+ master, username, "Secret123",
|
|
+ extra_args=["--uid", str(uid), "--gid", str(gid)]
|
|
+ )
|
|
+ tasks.kinit_admin(master)
|
|
+ try:
|
|
+ result = master.run_command(
|
|
+ ["ipa", "user-show", username, "--all", "--raw"]
|
|
+ )
|
|
+ assert result.returncode == 0, (
|
|
+ f"User not found: {result.stderr_text}"
|
|
+ )
|
|
+ assert "ipantsecurityidentifier" in \
|
|
+ result.stdout_text.lower(), (
|
|
+ "SID not found in user entry"
|
|
+ )
|
|
+ if hasattr(self, 'clients') and self.clients:
|
|
+ client = self.clients[0]
|
|
+ tasks.clear_sssd_cache(client)
|
|
+ result = client.run_command(
|
|
+ ["getent", "passwd", username], raiseonerr=False
|
|
+ )
|
|
+ assert result.returncode == 0, (
|
|
+ f"getent passwd failed: {result.stderr_text}"
|
|
+ )
|
|
+ assert str(uid) in result.stdout_text
|
|
+ assert str(gid) in result.stdout_text
|
|
+ finally:
|
|
+ tasks.user_del(master, username)
|
|
+
|
|
+ def test_create_group_with_32bit_gid(self):
|
|
+ """
|
|
+ Test that a group can be created with a GID from the 32-bit range.
|
|
+ """
|
|
+ master = self.master
|
|
+ groupname = 'grp32bit'
|
|
+ gid = self.get_next_32bit_id()
|
|
+ tasks.group_add(master, groupname, extra_args=["--gid", str(gid)])
|
|
+ try:
|
|
+ result = master.run_command(
|
|
+ ["ipa", "group-show", groupname, "--all", "--raw"]
|
|
+ )
|
|
+ assert result.returncode == 0
|
|
+ assert str(gid) in result.stdout_text, (
|
|
+ f"GID {gid} not in group entry"
|
|
+ )
|
|
+ finally:
|
|
+ tasks.group_del(master, groupname)
|
|
|
|
+ def test_user_in_group_with_32bit_ids(self):
|
|
+ """
|
|
+ Test user with 32-bit UID in a group with 32-bit GID.
|
|
+ """
|
|
master = self.master
|
|
- idrange = f"{master.domain.realm}_upper_32bit_range"
|
|
- id_base = 1 << 31
|
|
- id_length = (1 << 31) - 2
|
|
- uid = id_base + 1
|
|
- gid = id_base + 1
|
|
- master.run_command(
|
|
- [
|
|
- "ipa",
|
|
- "idrange-add",
|
|
- idrange,
|
|
- "--base-id", str(id_base),
|
|
- "--range-size", str(id_length),
|
|
- "--rid-base", str(int(id_base >> 3)),
|
|
- "--secondary-rid-base", str(int(id_base >> 3) + id_length),
|
|
- "--type=ipa-local"
|
|
- ]
|
|
+ groupname = 'grp32bit2'
|
|
+ username = 'user32bit'
|
|
+ uid = self.get_next_32bit_id()
|
|
+ gid = self.get_next_32bit_id()
|
|
+ tasks.group_add(master, groupname, extra_args=["--gid", str(gid)])
|
|
+ tasks.create_active_user(
|
|
+ master, username, "Secret123",
|
|
+ extra_args=["--uid", str(uid), "--gid", str(gid)]
|
|
)
|
|
+ tasks.kinit_admin(master)
|
|
+ try:
|
|
+ tasks.group_add_member(master, groupname, users=username)
|
|
+ result = master.run_command(
|
|
+ ["ipa", "group-show", groupname, "--all", "--raw"]
|
|
+ )
|
|
+ assert result.returncode == 0
|
|
+ assert username in result.stdout_text
|
|
+ assert f"gidnumber: {gid}" in result.stdout_text, (
|
|
+ f"GID {gid} not found in group entry"
|
|
+ )
|
|
+ assert "ipaNTSecurityIdentifier:" in result.stdout_text, (
|
|
+ "Group does not contain a SID"
|
|
+ )
|
|
+ result = master.run_command(
|
|
+ ["ipa", "user-show", username, "--all", "--raw"]
|
|
+ )
|
|
+ assert result.returncode == 0
|
|
+ finally:
|
|
+ master.run_command(
|
|
+ ["ipa", "group-remove-member", groupname,
|
|
+ "--users", username],
|
|
+ raiseonerr=False
|
|
+ )
|
|
+ tasks.user_del(master, username)
|
|
+ tasks.group_del(master, groupname)
|
|
|
|
- # We added new ID range, SIDGEN will only take it after
|
|
- # restarting a directory server instance.
|
|
- tasks.restart_ipa_server(master)
|
|
+ def test_ssh_login_with_32bit_id(self):
|
|
+ """
|
|
+ Test that a user with 32-bit UID/GID can kinit and log in via SSH
|
|
+ from the client to the master using GSSAPI (Kerberos).
|
|
+ """
|
|
+ client = self.clients[0]
|
|
+ master = self.master
|
|
+ testuser = 'sshuser32bit'
|
|
+ password = 'Secret123'
|
|
+ uid = self.get_next_32bit_id()
|
|
+ gid = self.get_next_32bit_id()
|
|
|
|
- # Clear SSSD cache to pick up new ID range
|
|
tasks.clear_sssd_cache(master)
|
|
+ tasks.create_active_user(
|
|
+ master, testuser, password,
|
|
+ extra_args=["--uid", str(uid), "--gid", str(gid)]
|
|
+ )
|
|
+ tasks.kinit_admin(master)
|
|
+ hbac_rule = "allow_ssh_32bit_test"
|
|
+ tasks.hbacrule_add(master, hbac_rule, extra_args=["--hostcat=all"])
|
|
+ tasks.hbacrule_add_user(master, hbac_rule, users=testuser)
|
|
+ tasks.hbacrule_add_service(master, hbac_rule, services="sshd")
|
|
+ try:
|
|
+ result = master.run_command(
|
|
+ ["ipa", "user-show", testuser, "--all", "--raw"]
|
|
+ )
|
|
+ assert result.returncode == 0, (
|
|
+ f"User {testuser} not found: {result.stderr_text}"
|
|
+ )
|
|
|
|
- tasks.user_add(master, "user", extra_args=[
|
|
- "--uid", str(uid), "--gid", str(gid)
|
|
- ])
|
|
+ tasks.clear_sssd_cache(client)
|
|
+ tasks.clear_sssd_cache(master)
|
|
+ tasks.kdestroy_all(client)
|
|
+ tasks.kinit_as_user(client, testuser, password)
|
|
+ result = client.run_command([
|
|
+ 'ssh', '-o', 'StrictHostKeyChecking=no', '-K',
|
|
+ '-l', testuser, master.hostname, 'echo login successful'
|
|
+ ], raiseonerr=False)
|
|
+ assert result.returncode == 0, (
|
|
+ "SSH (GSSAPI) from client to master failed: "
|
|
+ f"{result.stderr_text}"
|
|
+ )
|
|
+ assert 'login successful' in result.stdout_text, (
|
|
+ "SSH succeeded but expected output missing: "
|
|
+ f"{result.stdout_text}"
|
|
+ )
|
|
+ finally:
|
|
+ tasks.kdestroy_all(client)
|
|
+ master.run_command(
|
|
+ ["ipa", "hbacrule-del", hbac_rule], raiseonerr=False
|
|
+ )
|
|
+ tasks.kinit_admin(master)
|
|
+ tasks.user_del(master, testuser)
|
|
|
|
- result = master.run_command(
|
|
- ["ipa", "user-show", "user", "--all", "--raw"], raiseonerr=False
|
|
- )
|
|
- assert result.returncode == 0
|
|
- assert "ipaNTSecurityIdentifier:" in result.stdout_text
|
|
+ def test_32bit_id_replication(self):
|
|
+ """
|
|
+ Test that users with 32-bit IDs replicate correctly
|
|
+ """
|
|
+ master = self.master
|
|
+ replica = self.replicas[0]
|
|
+ tasks.kinit_admin(master)
|
|
+ testuser = 'repluser32bit'
|
|
+ uid = self.get_next_32bit_id()
|
|
+ gid = self.get_next_32bit_id()
|
|
|
|
- result = master.run_command(
|
|
- ["id", "user"], raiseonerr=False
|
|
+ tasks.clear_sssd_cache(master)
|
|
+
|
|
+ # Create user on master
|
|
+ tasks.create_active_user(
|
|
+ master, testuser, "Secret123",
|
|
+ extra_args=["--uid", str(uid), "--gid", str(gid)]
|
|
)
|
|
- assert result.returncode == 0
|
|
- assert str(uid) in result.stdout_text
|
|
+ tasks.kinit_admin(master)
|
|
+ try:
|
|
+ tasks.wait_for_replication(master.ldap_connect())
|
|
+
|
|
+ result = master.run_command(
|
|
+ ["ipa", "user-show", testuser, "--all", "--raw"],
|
|
+ raiseonerr=False
|
|
+ )
|
|
+ assert result.returncode == 0, (
|
|
+ f"User {testuser} not found on master"
|
|
+ )
|
|
+ assert str(uid) in result.stdout_text, (
|
|
+ f"UID {uid} not on master"
|
|
+ )
|
|
+
|
|
+ tasks.kinit_admin(replica)
|
|
+ result = replica.run_command(
|
|
+ ["ipa", "user-show", testuser, "--all", "--raw"],
|
|
+ raiseonerr=False
|
|
+ )
|
|
+ assert result.returncode == 0, (
|
|
+ f"User {testuser} not replicated to replica"
|
|
+ )
|
|
+ assert str(uid) in result.stdout_text, (
|
|
+ f"UID {uid} not on replica"
|
|
+ )
|
|
+ finally:
|
|
+ # Cleanup: Remove test user from master
|
|
+ tasks.kinit_admin(master)
|
|
+ tasks.user_del(master, testuser)
|
|
|
|
|
|
class Test32BitIdrangeInTrustEnv(Test32BitIdRanges, BaseTestTrust):
|
|
"""
|
|
Tests to check 32BitIdrange functionality
|
|
- in IPA-AD trust enviornment
|
|
+ in IPA-AD trust environment
|
|
"""
|
|
topology = 'line'
|
|
+ num_replicas = 1
|
|
num_ad_domains = 1
|
|
num_ad_subdomains = 0
|
|
num_ad_treedomains = 0
|
|
- num_clients = 0
|
|
+ num_clients = 1
|
|
|
|
@classmethod
|
|
def install(cls, mh):
|
|
- super(BaseTestTrust, cls).install(mh)
|
|
+ super(Test32BitIdrangeInTrustEnv, cls).install(mh)
|
|
cls.ad = cls.ads[0]
|
|
- cls.ad_domain = cls.ad.domain.name
|
|
tasks.configure_dns_for_trust(cls.master, cls.ad)
|
|
- tasks.install_adtrust(cls.master)
|
|
tasks.establish_trust_with_ad(cls.master, cls.ad.domain.name)
|
|
--
|
|
2.53.0
|
|
|