ipa-4.13.1-2
- Resolves: RHEL-146023 When using xmlrpc, ipa server failed with assert type(value) in (unicode, float, int, bool, type(None)) -Resolves: RHEL-145855 Include latest fixes in python3-ipatests package -Resolves: RHEL-88855 ipa uninstallation is failing with message "'NoneType' object has no attribute 'lower'" -Resolves: RHEL-43143 ipa-advise client script requires keytab (should just require root access on client system) -Resolves: RHEL-4895 ipa use systemd-sysusers -Resolves: RHEL-4823 Names of domains from a trusted forest should be compared case-insentive Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
parent
fd81c0a70e
commit
72f8d73c00
120
0001-ipatests-Move-expire_password-fixture-into-TestIPACo.patch
Normal file
120
0001-ipatests-Move-expire_password-fixture-into-TestIPACo.patch
Normal file
@ -0,0 +1,120 @@
|
||||
From 1c86c973c8dc778a71c8e2abf54ff37ececdd696 Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Tue, 13 Jan 2026 19:30:09 +0530
|
||||
Subject: [PATCH] ipatests: Move expire_password fixture into TestIPACommand
|
||||
class
|
||||
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
.../nightly_ipa-4-13_latest.yaml | 2 +-
|
||||
.../nightly_ipa-4-13_latest_selinux.yaml | 2 +-
|
||||
ipatests/test_integration/test_commands.py | 59 +++++++++----------
|
||||
3 files changed, 31 insertions(+), 32 deletions(-)
|
||||
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
index 210739d4b576882ee43e06d85bce819ff30d2357..aff55727e463207fb235ff340989491e62162149 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
@@ -1914,7 +1914,7 @@ jobs:
|
||||
class: RunPytest
|
||||
args:
|
||||
build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
- test_suite: test_integration/test_random_serial_numbers.py::TestIPACommand_RSN::test_certificate_out_write_to_file
|
||||
+ test_suite: test_integration/test_random_serial_numbers.py::TestIPACommand_RSN
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 5400
|
||||
topology: *master_1repl_1client
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
index 0fb7c050b97bf66645599fbff46b53c048211f96..e6c57ea060b3bb8bfdf8b6f981f8fd28e4a7d320 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
@@ -2066,7 +2066,7 @@ jobs:
|
||||
args:
|
||||
build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
selinux_enforcing: True
|
||||
- test_suite: test_integration/test_random_serial_numbers.py::TestIPACommand_RSN::test_certificate_out_write_to_file
|
||||
+ test_suite: test_integration/test_random_serial_numbers.py::TestIPACommand_RSN
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 5400
|
||||
topology: *master_1repl_1client
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 01001e4c92037599b075e9bca3ddda7d0c8e8ffa..eda98e92b6b494752d74c6381bdcb6d5c47a26d1 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -199,36 +199,6 @@ duplicatesubject = (
|
||||
duplicate_serial = "4097"
|
||||
|
||||
|
||||
-@pytest.fixture()
|
||||
-def expire_password():
|
||||
- """
|
||||
- Fixture to expire a user's password far into the future past
|
||||
- 2038, then revert time back.
|
||||
- """
|
||||
- hosts = dict()
|
||||
-
|
||||
- def _expire_password(host):
|
||||
- hosts['host'] = host
|
||||
- tasks.move_date(host, 'stop', '+20Years')
|
||||
- host.run_command(
|
||||
- ['ipactl', 'restart', '--ignore-service-failures']
|
||||
- )
|
||||
-
|
||||
- yield _expire_password
|
||||
-
|
||||
- host = hosts.pop('host')
|
||||
- # Prior to uninstall remove all the cert tracking to prevent
|
||||
- # errors from certmonger trying to check the status of certs
|
||||
- # that don't matter because we are uninstalling.
|
||||
- host.run_command(['systemctl', 'stop', 'certmonger'])
|
||||
- # Important: run_command with a str argument is able to
|
||||
- # perform shell expansion but run_command with a list of
|
||||
- # arguments is not
|
||||
- host.run_command('rm -fv ' + paths.CERTMONGER_REQUESTS_DIR + '*')
|
||||
- tasks.uninstall_master(host)
|
||||
- tasks.move_date(host, 'start', '-20Years')
|
||||
-
|
||||
-
|
||||
class TestIPACommand(IntegrationTest):
|
||||
"""
|
||||
A lot of commands can be executed against a single IPA installation
|
||||
@@ -239,6 +209,35 @@ class TestIPACommand(IntegrationTest):
|
||||
num_replicas = 1
|
||||
num_clients = 1
|
||||
|
||||
+ @pytest.fixture
|
||||
+ def expire_password(self):
|
||||
+ """
|
||||
+ Fixture to expire a user's password far into the future past
|
||||
+ 2038, then revert time back.
|
||||
+ """
|
||||
+ hosts = dict()
|
||||
+
|
||||
+ def _expire_password(host):
|
||||
+ hosts['host'] = host
|
||||
+ tasks.move_date(host, 'stop', '+20Years')
|
||||
+ host.run_command(
|
||||
+ ['ipactl', 'restart', '--ignore-service-failures']
|
||||
+ )
|
||||
+
|
||||
+ yield _expire_password
|
||||
+
|
||||
+ host = hosts.pop('host')
|
||||
+ # Prior to uninstall remove all the cert tracking to prevent
|
||||
+ # errors from certmonger trying to check the status of certs
|
||||
+ # that don't matter because we are uninstalling.
|
||||
+ host.run_command(['systemctl', 'stop', 'certmonger'])
|
||||
+ # Important: run_command with a str argument is able to
|
||||
+ # perform shell expansion but run_command with a list of
|
||||
+ # arguments is not
|
||||
+ host.run_command('rm -fv ' + paths.CERTMONGER_REQUESTS_DIR + '*')
|
||||
+ tasks.uninstall_master(host)
|
||||
+ tasks.move_date(host, 'start', '-20Years')
|
||||
+
|
||||
@pytest.fixture
|
||||
def pwpolicy_global(self):
|
||||
"""Fixture to change global password history policy and reset it"""
|
||||
--
|
||||
2.52.0
|
||||
|
||||
48
0002-ipatests-Fix-xfail-assertion-for-sssd-2.12.0.patch
Normal file
48
0002-ipatests-Fix-xfail-assertion-for-sssd-2.12.0.patch
Normal file
@ -0,0 +1,48 @@
|
||||
From ecc0efa96e3c446586425d470657de7f2d5376bf Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Mon, 19 Jan 2026 17:00:41 +0100
|
||||
Subject: [PATCH] ipatests: Fix xfail assertion for sssd 2.12.0
|
||||
|
||||
SSSD 2.12.0 provides fixes for
|
||||
https://github.com/SSSD/sssd/issues/5989
|
||||
https://github.com/SSSD/sssd/issues/7169
|
||||
|
||||
Update the condition expecting test failures in test_trust.py
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_trust.py | 10 ++++++----
|
||||
1 file changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
||||
index 13ad0afa4c1fb032d50f40cf7cb9b79283203225..0cab277c910a6d35f35b57e3068ee6f38706af59 100644
|
||||
--- a/ipatests/test_integration/test_trust.py
|
||||
+++ b/ipatests/test_integration/test_trust.py
|
||||
@@ -1219,9 +1219,10 @@ class TestNonPosixAutoPrivateGroup(BaseTestTrust):
|
||||
assert (uid == self.uid_override and gid == self.gid_override)
|
||||
test_group = self.clients[0].run_command(
|
||||
["id", nonposixuser]).stdout_text
|
||||
- cond2 = ((type == 'false'
|
||||
- and sssd_version >= tasks.parse_version("2.9.4"))
|
||||
- or type == 'hybrid')
|
||||
+ cond2 = (((type == 'false'
|
||||
+ and sssd_version >= tasks.parse_version("2.9.4"))
|
||||
+ or type == 'hybrid')
|
||||
+ and sssd_version < tasks.parse_version("2.12.0"))
|
||||
with xfail_context(cond2,
|
||||
'https://github.com/SSSD/sssd/issues/5989 '
|
||||
'and 7169'):
|
||||
@@ -1347,7 +1348,8 @@ class TestPosixAutoPrivateGroup(BaseTestTrust):
|
||||
and gid == self.gid_override)
|
||||
result = self.clients[0].run_command(['id', posixuser])
|
||||
sssd_version = tasks.get_sssd_version(self.clients[0])
|
||||
- bad_version = sssd_version >= tasks.parse_version("2.9.4")
|
||||
+ bad_version = (tasks.parse_version("2.9.4") <= sssd_version
|
||||
+ < tasks.parse_version("2.12.0"))
|
||||
with xfail_context(bad_version and type in ('false', 'hybrid'),
|
||||
"https://github.com/SSSD/sssd/issues/7169"):
|
||||
assert "10047(testgroup@{0})".format(
|
||||
--
|
||||
2.52.0
|
||||
|
||||
1279
0003-ipatests-Add-DNS-functional-integration-tests.patch
Normal file
1279
0003-ipatests-Add-DNS-functional-integration-tests.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,99 @@
|
||||
From 5756ed2af940378c16d9d52e083b8c4005d41a13 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Wed, 21 Jan 2026 17:19:18 +0100
|
||||
Subject: [PATCH] ipa-advise: smart card client script does not need krb ticket
|
||||
|
||||
The script generated by ipa-advise config-client-for-smart-card-auth
|
||||
currently requires a kerberos ticket because it calls ipa-certupdate.
|
||||
|
||||
Since IPA 4.9.0 and commit 1a09ce9, ipa-certupdate can be called
|
||||
without a ticket. Update the script so that it detects if it gets
|
||||
executed on a client recent enough to skip that requirement.
|
||||
|
||||
Update the test for config-client-for-smart-card-auth, do not
|
||||
call kinit admin on the client.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9923
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/advise/plugins/smart_card_auth.py | 22 ++++++++++++++++++++-
|
||||
ipatests/test_integration/test_advise.py | 10 +++++++---
|
||||
2 files changed, 28 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py
|
||||
index b79797dcaee0c881d3ef752a268ed520d96b433b..a0e50e9806f7843d2981141d8941d5e37f53c0cd 100644
|
||||
--- a/ipaserver/advise/plugins/smart_card_auth.py
|
||||
+++ b/ipaserver/advise/plugins/smart_card_auth.py
|
||||
@@ -34,6 +34,26 @@ class common_smart_card_auth_config(Advice):
|
||||
'Use kinit as privileged user to obtain Kerberos credentials'
|
||||
])
|
||||
|
||||
+ def check_ccache_not_empty_if_old_version(self):
|
||||
+ self.log.comment("On version before IPA 4.9, "
|
||||
+ "check that the credential cache is not empty")
|
||||
+ self.log.command(
|
||||
+ "python3 -c \"from ipapython.version import VERSION;"
|
||||
+ "from ipaplatform.tasks import tasks;"
|
||||
+ "exit(tasks.parse_ipa_version(VERSION) >= "
|
||||
+ "tasks.parse_ipa_version('4.9.0'))\"")
|
||||
+ with self.log.if_branch('[ "$?" -eq "0" ]'):
|
||||
+ self.log.exit_on_failed_command(
|
||||
+ 'klist',
|
||||
+ [
|
||||
+ "Credential cache is empty",
|
||||
+ 'Use kinit as privileged user to obtain Kerberos '
|
||||
+ 'credentials'
|
||||
+ ])
|
||||
+ with self.log.else_branch():
|
||||
+ self.log.command(
|
||||
+ "echo 'Version 4.9.0+ does not require Kerberos credentials'")
|
||||
+
|
||||
def check_and_set_ca_cert_paths(self):
|
||||
ca_paths_variable = self.smart_card_ca_certs_variable_name
|
||||
single_ca_path_variable = self.single_ca_cert_variable_name
|
||||
@@ -260,7 +280,7 @@ class config_client_for_smart_card_auth(common_smart_card_auth_config):
|
||||
def get_info(self):
|
||||
self.log.exit_on_nonroot_euid()
|
||||
self.check_and_set_ca_cert_paths()
|
||||
- self.check_ccache_not_empty()
|
||||
+ self.check_ccache_not_empty_if_old_version()
|
||||
self.check_and_remove_pam_pkcs11()
|
||||
self.install_opensc_and_dconf_packages()
|
||||
self.install_krb5_client_dependencies()
|
||||
diff --git a/ipatests/test_integration/test_advise.py b/ipatests/test_integration/test_advise.py
|
||||
index 3d5cadee319ebba14ebc43ebb1dc90a502e5d3b8..a336634ae9627133c5ad4dea4b1c43ffd726df10 100644
|
||||
--- a/ipatests/test_integration/test_advise.py
|
||||
+++ b/ipatests/test_integration/test_advise.py
|
||||
@@ -60,13 +60,17 @@ class TestAdvice(IntegrationTest):
|
||||
)
|
||||
tasks.install_client(cls.master, cls.clients[0])
|
||||
|
||||
- def execute_advise(self, host, advice_id, *args):
|
||||
+ def execute_advise(self, host, advice_id, *args, kinit=True):
|
||||
# ipa-advise script is only available on a server
|
||||
tasks.kinit_admin(self.master)
|
||||
advice = self.master.run_command(['ipa-advise', advice_id])
|
||||
# execute script on host (client or master)
|
||||
if host is not self.master:
|
||||
- tasks.kinit_admin(host)
|
||||
+ if kinit:
|
||||
+ tasks.kinit_admin(host)
|
||||
+ else:
|
||||
+ # Make sure we don't have any ticket
|
||||
+ tasks.kdestroy_all(host)
|
||||
filename = tasks.upload_temp_contents(host, advice.stdout_text)
|
||||
cmd = ['sh', filename]
|
||||
cmd.extend(args)
|
||||
@@ -181,7 +185,7 @@ class TestAdvice(IntegrationTest):
|
||||
ca_pem = ExternalCA().create_ca()
|
||||
ca_file = tasks.upload_temp_contents(client, ca_pem)
|
||||
try:
|
||||
- self.execute_advise(client, advice_id, ca_file)
|
||||
+ self.execute_advise(client, advice_id, ca_file, kinit=False)
|
||||
finally:
|
||||
client.run_command(['rm', '-f', ca_file])
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
177
0005-ipatests-Fix-resolver-state-tracking-in-enforced-DNS.patch
Normal file
177
0005-ipatests-Fix-resolver-state-tracking-in-enforced-DNS.patch
Normal file
@ -0,0 +1,177 @@
|
||||
From fd84cec77d18e0e608285ab712f8211b0b49a7fe Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Thu, 18 Dec 2025 17:13:29 +0530
|
||||
Subject: [PATCH] ipatests: Fix resolver state tracking in enforced DNS policy
|
||||
tests.
|
||||
|
||||
Use resolver.setup_resolver() instead of direct file manipulation to
|
||||
prevent state tracking failures in IDM-CI. Remove redundant manual
|
||||
resolv.conf changes and fix operation order.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9904
|
||||
Reviewed-By: Antonio Torres <antorres@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_edns.py | 70 ++++++++++++++++++--------
|
||||
1 file changed, 50 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_edns.py b/ipatests/test_integration/test_edns.py
|
||||
index d02ce45a1f9c0b04332e4ff4a055ab4c61b35eb4..54b7859da1b9fa320c07bdf29f79d2fdb292c337 100644
|
||||
--- a/ipatests/test_integration/test_edns.py
|
||||
+++ b/ipatests/test_integration/test_edns.py
|
||||
@@ -15,6 +15,9 @@ from ipatests.test_integration.test_dns import TestDNS
|
||||
from ipatests.pytest_ipa.integration.firewall import Firewall
|
||||
from ipaplatform.osinfo import osinfo
|
||||
from ipaplatform.paths import paths
|
||||
+from ipatests.pytest_ipa.integration.resolver import (
|
||||
+ resolver as detect_resolver
|
||||
+)
|
||||
|
||||
|
||||
def apply_enforced_dns_preconfig(host, master_ip, master_host,
|
||||
@@ -30,6 +33,9 @@ def apply_enforced_dns_preconfig(host, master_ip, master_host,
|
||||
ca_cert_path: Path to the CA certificate on the master
|
||||
dest_cert_name: Destination certificate filename in trust anchors
|
||||
"""
|
||||
+ # Backup resolver before making any changes (with original resolver type)
|
||||
+ host.resolver.backup()
|
||||
+
|
||||
# Get the network interface for routing
|
||||
iface_cmd = host.run_command([
|
||||
"bash", "-c",
|
||||
@@ -43,7 +49,8 @@ def apply_enforced_dns_preconfig(host, master_ip, master_host,
|
||||
host.put_file_contents(dest_ca_path, ca_cert_data)
|
||||
host.run_command(["update-ca-trust", "extract"])
|
||||
|
||||
- if osinfo.id in ['rhel', 'centos']:
|
||||
+ platform = tasks.get_platform(host)
|
||||
+ if platform in ['rhel', 'centos']:
|
||||
# RHEL/CentOS configuration
|
||||
# Install and configure dnsconfd
|
||||
tasks.install_packages(host, ['dnsconfd'])
|
||||
@@ -51,31 +58,60 @@ def apply_enforced_dns_preconfig(host, master_ip, master_host,
|
||||
host.run_command(["systemctl", "enable", "--now", "dnsconfd"])
|
||||
host.run_command(["nmcli", "g", "reload"])
|
||||
|
||||
- # Configure DNS over TLS via NetworkManager
|
||||
+ # Configure DNS over TLS via NetworkManager device-specific settings.
|
||||
+ # Device-specific DNS settings work alongside global resolver config.
|
||||
host.run_command([
|
||||
"nmcli", "device", "modify", iface,
|
||||
"ipv4.dns", f"dns+tls://{master_ip}"
|
||||
])
|
||||
|
||||
- elif osinfo.id == 'fedora':
|
||||
- # Fedora configuration
|
||||
+ elif platform == 'fedora':
|
||||
# Configure systemd-resolved for DNS over TLS
|
||||
host.run_command([
|
||||
- "ln", "-sf", "../run/systemd/resolve/stub-resolv.conf",
|
||||
+ "ln", "-sf", "/run/systemd/resolve/stub-resolv.conf",
|
||||
"/etc/resolv.conf"
|
||||
])
|
||||
+ # Restart systemd-resolved to ensure DoT settings are fully applied
|
||||
+ # and resolv.conf is updated
|
||||
host.run_command(["systemctl", "restart", "systemd-resolved"])
|
||||
-
|
||||
- # Configure DNS over TLS via systemd-resolve
|
||||
+ # Configure DNS over TLS via systemd-resolve per-interface settings
|
||||
+ # Per-interface DNS settings work alongside the global resolver config
|
||||
host.run_command([
|
||||
"systemd-resolve", "--set-dns", master_ip,
|
||||
"--set-dnsovertls=yes", f"--interface={iface}"
|
||||
])
|
||||
else:
|
||||
raise ValueError(
|
||||
- f"Unsupported OS for enforced DNS policy: {osinfo.id}"
|
||||
+ f"Unsupported OS for enforced DNS policy: {platform}"
|
||||
)
|
||||
|
||||
+ # Re-detect resolver since we may have changed the resolver type
|
||||
+ # (e.g., from PlainFileResolver to ResolvedResolver on Fedora)
|
||||
+ # This ensures state tracking works correctly with the new resolver type
|
||||
+ # Store the original resolver so we can restore from it during uninstall
|
||||
+ host._enforced_dns_original_resolver = host.resolver
|
||||
+ host.resolver = detect_resolver(host)
|
||||
+ host.resolver.current_state = host.resolver._get_state()
|
||||
+
|
||||
+
|
||||
+def restore_enforced_dns_resolver(host):
|
||||
+ """
|
||||
+ Restore resolver state after enforced DNS preconfig.
|
||||
+
|
||||
+ This restores the original resolver backup created by
|
||||
+ apply_enforced_dns_preconfig() and syncs host.resolver state.
|
||||
+ """
|
||||
+ if not hasattr(host, '_enforced_dns_original_resolver'):
|
||||
+ return
|
||||
+ original_resolver = host._enforced_dns_original_resolver
|
||||
+ if original_resolver.has_backups():
|
||||
+ original_resolver.current_state = original_resolver._get_state()
|
||||
+ original_resolver.restore()
|
||||
+ # Sync host.resolver state since original_resolver changed the config
|
||||
+ host.resolver.backups.clear()
|
||||
+ host.resolver.current_state = host.resolver._get_state()
|
||||
+ delattr(host, '_enforced_dns_original_resolver')
|
||||
+
|
||||
|
||||
def setup_dns_over_tls_environment(cls):
|
||||
"""
|
||||
@@ -451,17 +487,12 @@ class TestDNSOverTLS_EnforcedPolicy_IPA_CA(IntegrationTest):
|
||||
tasks.install_master(self.master, extra_args=args)
|
||||
|
||||
# Apply pre-configuration on client before installation
|
||||
+ # This configures the resolver with master IP and domain
|
||||
apply_enforced_dns_preconfig(
|
||||
self.clients[0], self.master.ip, self.master,
|
||||
paths.IPA_CA_CRT, "ca.crt"
|
||||
)
|
||||
|
||||
- # Configure client nameserver
|
||||
- self.clients[0].put_file_contents(
|
||||
- paths.RESOLV_CONF,
|
||||
- "nameserver %s" % self.master.ip
|
||||
- )
|
||||
-
|
||||
# Install client with enforced policy
|
||||
args = [
|
||||
"--dns-over-tls",
|
||||
@@ -506,6 +537,8 @@ class TestDNSOverTLS_EnforcedPolicy_IPA_CA(IntegrationTest):
|
||||
"""
|
||||
This test ensures that all hosts can be uninstalled correctly.
|
||||
"""
|
||||
+ for host in [self.clients[0], self.replicas[0]]:
|
||||
+ restore_enforced_dns_resolver(host)
|
||||
tasks.uninstall_client(self.clients[0])
|
||||
tasks.uninstall_replica(self.master, self.replicas[0])
|
||||
tasks.uninstall_master(self.master)
|
||||
@@ -559,17 +592,12 @@ class TestDNSOverTLS_EnforcedPolicy_External_CA(IntegrationTest):
|
||||
|
||||
# Apply pre-configuration on client before installation
|
||||
# (includes CA cert setup)
|
||||
+ # This configures the resolver with master IP and domain
|
||||
apply_enforced_dns_preconfig(
|
||||
self.clients[0], self.master.ip, self.master,
|
||||
cert_dest, "certificate.pem"
|
||||
)
|
||||
|
||||
- # Configure client nameserver
|
||||
- self.clients[0].put_file_contents(
|
||||
- paths.RESOLV_CONF,
|
||||
- "nameserver %s" % self.master.ip
|
||||
- )
|
||||
-
|
||||
# Install client with enforced policy
|
||||
args = [
|
||||
"--dns-over-tls",
|
||||
@@ -615,6 +643,8 @@ class TestDNSOverTLS_EnforcedPolicy_External_CA(IntegrationTest):
|
||||
"""
|
||||
This test ensures that all hosts can be uninstalled correctly.
|
||||
"""
|
||||
+ for host in [self.clients[0], self.replicas[0]]:
|
||||
+ restore_enforced_dns_resolver(host)
|
||||
tasks.uninstall_client(self.clients[0])
|
||||
tasks.uninstall_replica(self.master, self.replicas[0])
|
||||
tasks.uninstall_master(self.master)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
224
0006-freeipa.spec.in-Use-systemd-sysusers-to-setup-users-.patch
Normal file
224
0006-freeipa.spec.in-Use-systemd-sysusers-to-setup-users-.patch
Normal file
@ -0,0 +1,224 @@
|
||||
From 0800065ac5555dba102f05c947ca47b5dc9a81af Mon Sep 17 00:00:00 2001
|
||||
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Date: Fri, 23 Jan 2026 16:49:31 -0300
|
||||
Subject: [PATCH] freeipa.spec.in: Use systemd-sysusers to setup users and
|
||||
groups
|
||||
|
||||
System accounts for `kdcproxy` and `ipaapi` are now created with
|
||||
sysusers configuration and macros. User `apache` is updated, by
|
||||
adding it to group `ipaapi` using sysusers configuration.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9572
|
||||
|
||||
AI agent usage info:
|
||||
|
||||
The initial changes were created by Claude by providing the following
|
||||
context:
|
||||
|
||||
>> Add support for creating users through systemd-sysusers by creating
|
||||
>> a folder init/sysusersd, similar to init/tmpfilesd, changing install
|
||||
>> paths in init/sysusersd/Makefile.am, adding configure option
|
||||
>> --with-systemdsysusersdir similar to --with-systemdtmpfilesdir, and
|
||||
>> adding a new file init/sysusersd/freeeipo.sysusers.in with the
|
||||
>> contents:
|
||||
>> ```
|
||||
>> # system accounts for IPA
|
||||
>> u! kdcproxy - "IPA KDC Proxy Uer"
|
||||
>> u! ipaapi - "IPA Framework User"
|
||||
>> # - add Apache HTTPd user to ipaapi group
|
||||
>> m apache ipaapi
|
||||
>> ```
|
||||
>> and updating de spec file freeipa.spec.in
|
||||
|
||||
LLM model used was Claude Sonnet 4.5, and a CLAUDE.md file was
|
||||
automatically created by claude based on the freeipa repository.
|
||||
No custom context was available for the agent.
|
||||
|
||||
Assisted-by: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
configure.ac | 42 ++++++++++++++++++++++++++------------
|
||||
freeipa.spec.in | 16 +++------------
|
||||
init/Makefile.am | 2 +-
|
||||
init/sysusersd/Makefile.am | 12 +++++++++++
|
||||
init/sysusersd/ipa.conf.in | 8 ++++++++
|
||||
5 files changed, 53 insertions(+), 27 deletions(-)
|
||||
create mode 100644 init/sysusersd/Makefile.am
|
||||
create mode 100644 init/sysusersd/ipa.conf.in
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 8b9adec1559c8831ef39c27860c1d31496ec5474..b0462bf779dedb7c2fe59494d4eb64a6dd121b1a 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -267,6 +267,13 @@ AC_ARG_WITH([systemdtmpfilesdir],
|
||||
[systemdtmpfilesdir=$($PKG_CONFIG --define-variable=prefix='${prefix}' --variable=tmpfilesdir systemd)])
|
||||
AC_SUBST([systemdtmpfilesdir])
|
||||
|
||||
+AC_ARG_WITH([systemdsysusersdir],
|
||||
+ AS_HELP_STRING([--with-systemdsysusersdir=DIR],
|
||||
+ [Directory for systemd-sysusers configuration files]),
|
||||
+ [systemdsysusersdir=$with_systemdsysusersdir],
|
||||
+ [systemdsysusersdir=$($PKG_CONFIG --define-variable=prefix='${prefix}' --variable=sysusersdir systemd)])
|
||||
+AC_SUBST([systemdsysusersdir])
|
||||
+
|
||||
AC_ARG_WITH([systemdcatalogdir],
|
||||
AS_HELP_STRING([--with-systemdcatalogdir=DIR],
|
||||
[Directory for systemd journal catalog files]),
|
||||
@@ -398,22 +405,29 @@ AC_SUBST([IPAPLATFORM])
|
||||
AC_MSG_RESULT([${IPAPLATFORM}])
|
||||
|
||||
if test "x${IPAPLATFORM}" == "xdebian"; then
|
||||
- HTTPD_GROUP="www-data"
|
||||
- KRB5KDC_SERVICE="krb5-kdc.service"
|
||||
- NAMED_GROUP="bind"
|
||||
- ODS_USER="opendnssec"
|
||||
- ODS_GROUP="opendnssec"
|
||||
- # see https://www.debian.org/doc/packaging-manuals/python-policy/ap-packaging_tools.html
|
||||
- PYTHON_INSTALL_EXTRA_OPTIONS="--install-layout=deb"
|
||||
+ dnl Ubuntu http user is www-data
|
||||
+ HTTPD_USER="www-data"
|
||||
+ HTTPD_GROUP="www-data"
|
||||
+ KRB5KDC_SERVICE="krb5-kdc.service"
|
||||
+ NAMED_GROUP="bind"
|
||||
+ ODS_USER="opendnssec"
|
||||
+ ODS_GROUP="opendnssec"
|
||||
+ # see https://www.debian.org/doc/packaging-manuals/python-policy/ap-packaging_tools.html
|
||||
+ PYTHON_INSTALL_EXTRA_OPTIONS="--install-layout=deb"
|
||||
else
|
||||
- HTTPD_GROUP="apache"
|
||||
- KRB5KDC_SERVICE="krb5kdc.service"
|
||||
- NAMED_GROUP="named"
|
||||
- ODS_USER="ods"
|
||||
- ODS_GROUP="ods"
|
||||
- PYTHON_INSTALL_EXTRA_OPTIONS=""
|
||||
+ HTTPD_USER="apache"
|
||||
+ HTTPD_GROUP="apache"
|
||||
+ KRB5KDC_SERVICE="krb5kdc.service"
|
||||
+ NAMED_GROUP="named"
|
||||
+ ODS_USER="ods"
|
||||
+ ODS_GROUP="ods"
|
||||
+ PYTHON_INSTALL_EXTRA_OPTIONS=""
|
||||
fi
|
||||
|
||||
+AC_MSG_CHECKING([HTTPD_USER])
|
||||
+AC_SUBST([HTTPD_USER])
|
||||
+AC_MSG_RESULT([${HTTPD_USER}])
|
||||
+
|
||||
AC_MSG_CHECKING([HTTPD_GROUP])
|
||||
AC_SUBST([HTTPD_GROUP])
|
||||
AC_MSG_RESULT([${HTTPD_GROUP}])
|
||||
@@ -654,6 +668,7 @@ AC_CONFIG_FILES([
|
||||
daemons/ipa-slapi-plugins/topology/Makefile
|
||||
init/systemd/Makefile
|
||||
init/tmpfilesd/Makefile
|
||||
+ init/sysusersd/Makefile
|
||||
init/Makefile
|
||||
install/Makefile
|
||||
install/certmonger/Makefile
|
||||
@@ -736,6 +751,7 @@ AM_COND_IF([ENABLE_SERVER], [
|
||||
KRAD libs: ${KRAD_LIBS}
|
||||
krb5rundir: ${krb5rundir}
|
||||
systemdtmpfilesdir: ${systemdtmpfilesdir}
|
||||
+ systemdsysusersdir: ${systemdsysusersdir}
|
||||
build mode: server & client"
|
||||
], [
|
||||
echo "\
|
||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||
index f3b45a5308f93928a4d4bb4cbb2ae96c487cf88a..48912185073472c11f08d000dacf3a0b7f2ec668 100644
|
||||
--- a/freeipa.spec.in
|
||||
+++ b/freeipa.spec.in
|
||||
@@ -620,7 +620,7 @@ Requires: systemd-units >= %{systemd_version}
|
||||
Requires: system-logos-ipa >= 80.4
|
||||
%endif
|
||||
|
||||
-# The list below is automatically generated by `fix-spec.sh -i`
|
||||
+# The list below is automatically generated by `fix-spec.sh -i`
|
||||
# from the install/freeipa-webui
|
||||
Provides: bundled(npm(attr-accept)) = 2.2.5
|
||||
Provides: bundled(npm(cookie)) = 1.0.2
|
||||
@@ -1274,6 +1274,7 @@ fi
|
||||
/bin/systemctl reload-or-try-restart dbus
|
||||
/bin/systemctl reload-or-try-restart oddjobd
|
||||
|
||||
+%sysusers_create %{_sysusersdir}/ipa.conf
|
||||
%tmpfiles_create ipa.conf
|
||||
%journal_catalog_update
|
||||
|
||||
@@ -1331,18 +1332,6 @@ if [ -e /usr/sbin/ipa_kpasswd ]; then
|
||||
fi
|
||||
|
||||
|
||||
-%pre server-common
|
||||
-# create users and groups
|
||||
-# create kdcproxy group and user
|
||||
-getent group kdcproxy >/dev/null || groupadd -f -r kdcproxy
|
||||
-getent passwd kdcproxy >/dev/null || useradd -r -g kdcproxy -s /sbin/nologin -d / -c "IPA KDC Proxy User" kdcproxy
|
||||
-# create ipaapi group and user
|
||||
-getent group ipaapi >/dev/null || groupadd -f -r ipaapi
|
||||
-getent passwd ipaapi >/dev/null || useradd -r -g ipaapi -s /sbin/nologin -d / -c "IPA Framework User" ipaapi
|
||||
-# add apache to ipaaapi group
|
||||
-id -Gn apache | grep '\bipaapi\b' >/dev/null || usermod apache -a -G ipaapi
|
||||
-
|
||||
-
|
||||
%post server-dns
|
||||
%systemd_post ipa-dnskeysyncd.service ipa-ods-exporter.socket ipa-ods-exporter.service
|
||||
|
||||
@@ -1729,6 +1718,7 @@ fi
|
||||
%dir %attr(0755,root,root) %{_sysconfdir}/ipa/kdcproxy
|
||||
%config(noreplace) %{_sysconfdir}/ipa/kdcproxy/kdcproxy.conf
|
||||
# NOTE: systemd specific section
|
||||
+%{_sysusersdir}/ipa.conf
|
||||
%{_tmpfilesdir}/ipa.conf
|
||||
%attr(644,root,root) %{_unitdir}/ipa-custodia.service
|
||||
%ghost %attr(644,root,root) %{etc_systemd_dir}/httpd.d/ipa.conf
|
||||
diff --git a/init/Makefile.am b/init/Makefile.am
|
||||
index 8f4d1d0a8f7e9739cf7587de6e000dd027a85146..1d4a85ab20e892c8a7c428b84a6393d29e9616e5 100644
|
||||
--- a/init/Makefile.am
|
||||
+++ b/init/Makefile.am
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
AUTOMAKE_OPTIONS = 1.7
|
||||
|
||||
-SUBDIRS = systemd tmpfilesd
|
||||
+SUBDIRS = systemd tmpfilesd sysusersd
|
||||
|
||||
dist_sysconfenv_DATA = \
|
||||
ipa-dnskeysyncd \
|
||||
diff --git a/init/sysusersd/Makefile.am b/init/sysusersd/Makefile.am
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..8577255a61ac796353995d3d1f99de195f9bd7c0
|
||||
--- /dev/null
|
||||
+++ b/init/sysusersd/Makefile.am
|
||||
@@ -0,0 +1,12 @@
|
||||
+dist_noinst_DATA = \
|
||||
+ ipa.conf.in
|
||||
+
|
||||
+systemdsysusers_DATA = \
|
||||
+ ipa.conf
|
||||
+
|
||||
+CLEANFILES = $(systemdsysusers_DATA)
|
||||
+
|
||||
+%: %.in Makefile
|
||||
+ sed \
|
||||
+ -e 's|@HTTPD_USER[@]|$(HTTPD_USER)|g' \
|
||||
+ '$(srcdir)/$@.in' >$@
|
||||
diff --git a/init/sysusersd/ipa.conf.in b/init/sysusersd/ipa.conf.in
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..dcddfc2fc7969b86913ffcd8c397152e4f800fda
|
||||
--- /dev/null
|
||||
+++ b/init/sysusersd/ipa.conf.in
|
||||
@@ -0,0 +1,8 @@
|
||||
+# IPA KDC Proxy user and group
|
||||
+u! kdcproxy - "IPA KDC Proxy User"
|
||||
+
|
||||
+# IPA API user and group
|
||||
+u! ipaapi - "IPA API User"
|
||||
+
|
||||
+# - add Apache system account to ipaapi group (platform-specific)
|
||||
+m @HTTPD_USER@ ipaapi
|
||||
--
|
||||
2.52.0
|
||||
|
||||
196
0007-ipatests-add-Random-Password-based-replica-promotion.patch
Normal file
196
0007-ipatests-add-Random-Password-based-replica-promotion.patch
Normal file
@ -0,0 +1,196 @@
|
||||
From a55f9185c96457bdffe9099ddde39ec696f1f998 Mon Sep 17 00:00:00 2001
|
||||
From: Anuja More <amore@redhat.com>
|
||||
Date: Tue, 6 Jan 2026 18:30:06 +0530
|
||||
Subject: [PATCH] ipatests: add Random Password based replica promotion
|
||||
coverage
|
||||
|
||||
Added missing test coverage for :
|
||||
- Installing IPA replica server using random password.
|
||||
- Installing IPA replica server using random password installed client
|
||||
|
||||
- Automated with Cursor+Claude
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9922
|
||||
|
||||
Signed-off-by: Anuja More <amore@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
.../nightly_ipa-4-13_latest.yaml | 12 +++
|
||||
.../nightly_ipa-4-13_latest_selinux.yaml | 13 +++
|
||||
ipatests/pytest_ipa/integration/tasks.py | 15 ++++
|
||||
.../test_replica_promotion.py | 87 +++++++++++++++++++
|
||||
4 files changed, 127 insertions(+)
|
||||
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
index aff55727e463207fb235ff340989491e62162149..c61701ef5f88760f1d6fc36d4acce453a22b6f8f 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
@@ -1000,6 +1000,18 @@ jobs:
|
||||
timeout: 7200
|
||||
topology: *ad_master_1repl_1client
|
||||
|
||||
+ fedora-latest-ipa-4-13/test_replica_promotion_TestReplicaPromotionRandomPassword:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ test_suite: test_integration/test_replica_promotion.py::TestReplicaPromotionRandomPassword
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 7200
|
||||
+ topology: *master_1repl
|
||||
+
|
||||
fedora-latest-ipa-4-13/test_upgrade:
|
||||
requires: [fedora-latest-ipa-4-13/build]
|
||||
priority: 50
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
index e6c57ea060b3bb8bfdf8b6f981f8fd28e4a7d320..9b96f3e857e2125478b45632d8d58e42b6e92668 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
@@ -1078,6 +1078,19 @@ jobs:
|
||||
timeout: 7200
|
||||
topology: *ad_master_1repl_1client
|
||||
|
||||
+ fedora-latest-ipa-4-13/test_replica_promotion_TestReplicaPromotionRandomPassword:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ selinux_enforcing: True
|
||||
+ test_suite: test_integration/test_replica_promotion.py::TestReplicaPromotionRandomPassword
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 7200
|
||||
+ topology: *master_1repl
|
||||
+
|
||||
fedora-latest-ipa-4-13/test_upgrade:
|
||||
requires: [fedora-latest-ipa-4-13/build]
|
||||
priority: 50
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index 32ac5cbc2c6fe87850dfb15c1d5beae6fa648dfb..ff2ea9792d04ebd2e6bd7bb3b51d97f35cb3fbfb 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -3340,3 +3340,18 @@ def service_control_dirsrv(host, function='restart'):
|
||||
instance = realm_to_serverid(host.domain.realm)
|
||||
cmd = host.run_command(['systemctl', function, f"dirsrv@{instance}"])
|
||||
assert cmd.returncode == 0
|
||||
+
|
||||
+
|
||||
+def host_add_with_random_password(host, new_host):
|
||||
+ """
|
||||
+ Add a new host with a random password and return the generated password.
|
||||
+ """
|
||||
+ kinit_admin(host)
|
||||
+ cmd = host.run_command(
|
||||
+ ['ipa', 'host-add', new_host.hostname, '--random']
|
||||
+ )
|
||||
+ result = re.search("Random password: (?P<password>.*$)",
|
||||
+ cmd.stdout_text,
|
||||
+ re.MULTILINE)
|
||||
+ randpasswd1 = result.group('password')
|
||||
+ return randpasswd1
|
||||
diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
|
||||
index 76d6aa24e2ab3d88b7013e0d107d0e27ae7f3426..f8c8414eefbc015cfc0947de575ea349a65a5e73 100644
|
||||
--- a/ipatests/test_integration/test_replica_promotion.py
|
||||
+++ b/ipatests/test_integration/test_replica_promotion.py
|
||||
@@ -1368,3 +1368,90 @@ class TestReplicaConn(IntegrationTest):
|
||||
logs = self.replica.get_file_contents(paths.IPAREPLICA_CONNCHECK_LOG)
|
||||
error = "not allowed to perform server connection check"
|
||||
assert error.encode() not in logs
|
||||
+
|
||||
+
|
||||
+class TestReplicaPromotionRandomPassword(IntegrationTest):
|
||||
+ """
|
||||
+ Test installation of a replica using Random Password
|
||||
+ (one step install and two-steps installation
|
||||
+ with client and promotion).
|
||||
+ """
|
||||
+ num_replicas = 1
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ tasks.install_master(cls.master, setup_dns=True)
|
||||
+ cls.replicas[0].resolver.backup()
|
||||
+ nameservers = cls.master.ip
|
||||
+ cls.replicas[0].resolver.setup_resolver(
|
||||
+ nameservers, cls.master.domain.name
|
||||
+ )
|
||||
+
|
||||
+ @replicas_cleanup
|
||||
+ def test_replica_random_password_install(self):
|
||||
+ """
|
||||
+ Installing IPA replica server using Random Password.
|
||||
+
|
||||
+ Steps:
|
||||
+ 1. Ensure replica host/server entries are clean and add DNS A record.
|
||||
+ 2. Add the replica host with a random password and add it to
|
||||
+ the ipaservers hostgroup.
|
||||
+ 3. Install the replica using random password.
|
||||
+ """
|
||||
+ replica = self.replicas[0]
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.add_a_record(self.master, replica)
|
||||
+ randpasswd = tasks.host_add_with_random_password(self.master,
|
||||
+ replica)
|
||||
+ self.master.run_command([
|
||||
+ 'ipa', 'hostgroup-add-member', '--hosts',
|
||||
+ replica.hostname, 'ipaservers'
|
||||
+ ])
|
||||
+ replica.run_command(
|
||||
+ ['ipa-replica-install', '-p', randpasswd, '-U']
|
||||
+ )
|
||||
+
|
||||
+ @replicas_cleanup
|
||||
+ def test_replica_two_step_install(self):
|
||||
+ """
|
||||
+ Installing IPA replica server using Random Password installed client
|
||||
+
|
||||
+ Steps:
|
||||
+ 1. Ensure replica host/server entries are clean and add DNS A record.
|
||||
+ 2. Add the replica host with a random password and add it to
|
||||
+ the ipaservers hostgroup.
|
||||
+ 3. Install the IPA client using the Random Password.
|
||||
+ 4. Promote the client to a replica.
|
||||
+ 5. Install CA on the replica and verify the server role.
|
||||
+ """
|
||||
+ replica = self.replicas[0]
|
||||
+ replica.resolver.backup()
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.add_a_record(self.master, replica)
|
||||
+ randpasswd = tasks.host_add_with_random_password(self.master,
|
||||
+ replica)
|
||||
+ self.master.run_command([
|
||||
+ 'ipa', 'hostgroup-add-member', '--hosts',
|
||||
+ replica.hostname, 'ipaservers'
|
||||
+ ])
|
||||
+ replica.resolver.setup_resolver(
|
||||
+ self.master.ip, self.master.domain.name
|
||||
+ )
|
||||
+ replica.run_command(
|
||||
+ ['ipa-client-install', '-w', randpasswd, '-U']
|
||||
+ )
|
||||
+ Firewall(replica).enable_services(["freeipa-ldap",
|
||||
+ "freeipa-ldaps"])
|
||||
+ replica.run_command(['ipa-replica-install', '-U'])
|
||||
+ tasks.kinit_admin(replica)
|
||||
+ replica.run_command([
|
||||
+ 'ipa-ca-install', '-p',
|
||||
+ self.master.config.admin_password,
|
||||
+ '-w', self.master.config.admin_password
|
||||
+ ])
|
||||
+ result = self.replicas[0].run_command([
|
||||
+ 'ipa', 'server-role-find',
|
||||
+ '--server', self.replicas[0].hostname,
|
||||
+ '--role', 'CA server'
|
||||
+ ])
|
||||
+ assert 'Role status: enabled' in result.stdout_text
|
||||
--
|
||||
2.52.0
|
||||
|
||||
760
0008-ipatests-Add-integration-tests-for-ipa-join-command.patch
Normal file
760
0008-ipatests-Add-integration-tests-for-ipa-join-command.patch
Normal file
@ -0,0 +1,760 @@
|
||||
From 7cb2c5d38a84eb31aa9ddd20cf9dc0b6d90fa242 Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Tue, 27 Jan 2026 18:45:53 +0530
|
||||
Subject: [PATCH] ipatests: Add integration tests for ipa-join command
|
||||
|
||||
Add tests for ipa-join command covering hostname, server, keytab,
|
||||
and bindpw options with positive and negative scenarios.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9930
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/prci_definitions/gating.yaml | 12 +
|
||||
.../nightly_ipa-4-13_latest.yaml | 12 +
|
||||
.../nightly_ipa-4-13_latest_selinux.yaml | 13 +
|
||||
ipatests/pytest_ipa/integration/tasks.py | 49 ++
|
||||
ipatests/test_integration/test_ipa_join.py | 614 ++++++++++++++++++
|
||||
5 files changed, 700 insertions(+)
|
||||
create mode 100644 ipatests/test_integration/test_ipa_join.py
|
||||
|
||||
diff --git a/ipatests/prci_definitions/gating.yaml b/ipatests/prci_definitions/gating.yaml
|
||||
index 0c2a0dafa9de12add8959d9974f080ba8bec0706..182f2c5a097856d22336fa66a0876bdfcf3f3f8d 100644
|
||||
--- a/ipatests/prci_definitions/gating.yaml
|
||||
+++ b/ipatests/prci_definitions/gating.yaml
|
||||
@@ -394,3 +394,15 @@ jobs:
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 7200
|
||||
topology: *master_3client
|
||||
+
|
||||
+ fedora-latest-ipa-4-13/test_ipa_join:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 100
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ test_suite: test_integration/test_ipa_join.py
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 3600
|
||||
+ topology: *master_1repl_1client
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
index c61701ef5f88760f1d6fc36d4acce453a22b6f8f..aba33a5a05185460305c7516c93ae25c60f9dda7 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
@@ -2292,3 +2292,15 @@ jobs:
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 2400
|
||||
topology: *ipaserver
|
||||
+
|
||||
+ fedora-latest-ipa-4-13/test_ipa_join:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ test_suite: test_integration/test_ipa_join.py
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 3600
|
||||
+ topology: *master_1repl_1client
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
index 9b96f3e857e2125478b45632d8d58e42b6e92668..7184b722076ba2cab7d782d990a9bc218158a09f 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
@@ -2476,3 +2476,16 @@ jobs:
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 2400
|
||||
topology: *ipaserver
|
||||
+
|
||||
+ fedora-latest-ipa-4-13/test_ipa_join:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ selinux_enforcing: True
|
||||
+ test_suite: test_integration/test_ipa_join.py
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 3600
|
||||
+ topology: *master_1repl_1client
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index ff2ea9792d04ebd2e6bd7bb3b51d97f35cb3fbfb..47330d6d93401485e4eb7b2501cf5ea37498d719 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -3355,3 +3355,52 @@ def host_add_with_random_password(host, new_host):
|
||||
re.MULTILINE)
|
||||
randpasswd1 = result.group('password')
|
||||
return randpasswd1
|
||||
+
|
||||
+
|
||||
+def ipa_join(host, *extra_args, raiseonerr=True):
|
||||
+ """Run ipa-join command.
|
||||
+
|
||||
+ :param host: The host to run command on
|
||||
+ :param extra_args: Additional arguments (variable positional args)
|
||||
+ e.g., '--hostname=client.example.com',
|
||||
+ '--server=master.example.com',
|
||||
+ '--keytab=/tmp/test.keytab',
|
||||
+ '--bindpw=password',
|
||||
+ '-u' (for unenroll)
|
||||
+ :param raiseonerr: If True, raise exception on command failure
|
||||
+ :return: Command result object
|
||||
+ """
|
||||
+ command = ['ipa-join']
|
||||
+ command.extend(extra_args)
|
||||
+ return host.run_command(command, raiseonerr=raiseonerr)
|
||||
+
|
||||
+
|
||||
+def host_del(host, hostname, *extra_args, raiseonerr=True):
|
||||
+ """Delete a host from IPA.
|
||||
+
|
||||
+ :param host: The IPA host to run command on
|
||||
+ :param hostname: Hostname to delete
|
||||
+ :param extra_args: Additional arguments (variable positional args)
|
||||
+ :param raiseonerr: If True, raise exception on command failure
|
||||
+ :return: Command result object
|
||||
+ """
|
||||
+ command = ['ipa', 'host-del', hostname]
|
||||
+ command.extend(extra_args)
|
||||
+ return host.run_command(command, raiseonerr=raiseonerr)
|
||||
+
|
||||
+
|
||||
+def host_add(host, hostname, *extra_args, password=None, raiseonerr=True):
|
||||
+ """Add a host to IPA.
|
||||
+
|
||||
+ :param host: The IPA host to run command on
|
||||
+ :param hostname: Hostname to add
|
||||
+ :param extra_args: Additional arguments (variable positional args)
|
||||
+ :param password: OTP/enrollment password for the host (optional)
|
||||
+ :param raiseonerr: If True, raise exception on command failure
|
||||
+ :return: Command result object
|
||||
+ """
|
||||
+ command = ['ipa', 'host-add', hostname]
|
||||
+ if password:
|
||||
+ command.append(f'--password={password}')
|
||||
+ command.extend(extra_args)
|
||||
+ return host.run_command(command, raiseonerr=raiseonerr)
|
||||
diff --git a/ipatests/test_integration/test_ipa_join.py b/ipatests/test_integration/test_ipa_join.py
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1f7592aec8db1bfd048ec574d06d25bc24373499
|
||||
--- /dev/null
|
||||
+++ b/ipatests/test_integration/test_ipa_join.py
|
||||
@@ -0,0 +1,614 @@
|
||||
+#
|
||||
+# Copyright (C) 2026 FreeIPA Contributors see COPYING for license
|
||||
+#
|
||||
+
|
||||
+"""
|
||||
+Tests for ipa-join command functionality.
|
||||
+
|
||||
+This module tests various combinations of ipa-join options including:
|
||||
+- hostname
|
||||
+- server
|
||||
+- keytab
|
||||
+- bindpw (OTP/enrollment password)
|
||||
+- unenroll
|
||||
+
|
||||
+Ported from the shell-based test suite (t.ipajoin.sh and t.ipaotp.sh).
|
||||
+"""
|
||||
+
|
||||
+from __future__ import absolute_import
|
||||
+
|
||||
+from ipapython.ipautil import ipa_generate_password
|
||||
+from ipatests.pytest_ipa.integration import tasks
|
||||
+from ipatests.test_integration.base import IntegrationTest
|
||||
+
|
||||
+
|
||||
+# Constants
|
||||
+OTP = ipa_generate_password(special=None)
|
||||
+INVALID_PASSWORD = "WrongPassword"
|
||||
+INVALID_SERVER = "No.Such.IPA.Server.Domain.com"
|
||||
+TEST_KEYTAB = "/tmp/ipajoin.test.keytab"
|
||||
+
|
||||
+# Error messages
|
||||
+ERR_SASL_BIND_FAILED = "SASL Bind failed"
|
||||
+ERR_UNAUTHENTICATED_BIND = "Unauthenticated binds are not allowed"
|
||||
+ERR_COULD_NOT_RESOLVE = "JSON-RPC call failed: Could not resolve hostname"
|
||||
+ERR_UNABLE_ROOT_DN = "Unable to determine root DN"
|
||||
+ERR_NO_CONFIG = "Unable to determine IPA server from /etc/ipa/default.conf"
|
||||
+ERR_PREAUTH_FAILED = "Generic preauthentication failure"
|
||||
+
|
||||
+# Exit codes
|
||||
+EXIT_SUCCESS = 0
|
||||
+EXIT_GENERAL_ERROR = 1
|
||||
+EXIT_PREAUTH_ERROR = 19
|
||||
+EXIT_ROOT_DN_ERROR = 14
|
||||
+EXIT_SASL_BIND_FAILED = 15
|
||||
+EXIT_RESOLVE_ERROR = 17
|
||||
+
|
||||
+
|
||||
+class TestIPAJoin(IntegrationTest):
|
||||
+ """Tests for ipa-join command functionality.
|
||||
+
|
||||
+ This test class covers various ipa-join scenarios including:
|
||||
+ - Basic enrollment and unenrollment
|
||||
+ - Using hostname, server, keytab, and bindpw options
|
||||
+ - Positive and negative test cases
|
||||
+ - OTP (one-time password) enrollment tests
|
||||
+
|
||||
+ Tests require one master and one client.
|
||||
+ """
|
||||
+
|
||||
+ topology = 'line'
|
||||
+ num_clients = 1
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ tasks.install_master(cls.master, setup_dns=True)
|
||||
+ tasks.install_client(cls.master, cls.clients[0])
|
||||
+
|
||||
+ @classmethod
|
||||
+ def uninstall(cls, mh):
|
||||
+ # Cleanup test keytab if exists
|
||||
+ cls.clients[0].run_command(
|
||||
+ ['rm', '-f', TEST_KEYTAB],
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+ tasks.uninstall_client(cls.clients[0])
|
||||
+ tasks.uninstall_master(cls.master)
|
||||
+
|
||||
+ # =========================================================================
|
||||
+ # ipa-join basic tests
|
||||
+ # =========================================================================
|
||||
+
|
||||
+ def test_unenroll(self):
|
||||
+ """Test ipa-join --unenroll option."""
|
||||
+ result = tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+
|
||||
+ def test_unenroll_already_unenrolled(self):
|
||||
+ """Test ipa-join -u on an already unenrolled client.
|
||||
+
|
||||
+ When trying to unenroll a client that is not enrolled,
|
||||
+ ipa-join should fail with a preauthentication error.
|
||||
+ """
|
||||
+ # Client is already unenrolled from previous test
|
||||
+ result = tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ assert result.returncode == EXIT_PREAUTH_ERROR
|
||||
+ assert ERR_PREAUTH_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_with_kerberos(self):
|
||||
+ """Test ipa-join with --hostname using Kerberos auth."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_bindpw_invalid(self):
|
||||
+ """Test ipa-join with hostname and invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname and valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname and keytab using Kerberos."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_keytab_bindpw_invalid(self):
|
||||
+ """Test ipa-join with hostname, keytab, and invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_keytab_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, keytab, and valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_invalid_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname and invalid server."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_RESOLVE_ERROR
|
||||
+ assert ERR_COULD_NOT_RESOLVE in result.stderr_text
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_invalid_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, invalid server, and valid OTP."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--bindpw={OTP}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_ROOT_DN_ERROR
|
||||
+ assert ERR_UNABLE_ROOT_DN in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_server_invalid_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname, invalid server, keytab."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_RESOLVE_ERROR
|
||||
+ assert ERR_COULD_NOT_RESOLVE in result.stderr_text
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_invalid_keytab_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, invalid server, keytab, valid OTP."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={OTP}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_ROOT_DN_ERROR
|
||||
+ assert ERR_UNABLE_ROOT_DN in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_server_valid_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname and valid server."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_valid_bindpw_invalid(self):
|
||||
+ """Test ipa-join with hostname, valid server, invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_server_valid_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, valid server, valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_valid_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname, valid server, keytab."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_valid_keytab_bindpw_invalid(self):
|
||||
+ """Test ipa-join with hostname, valid server, keytab, bad bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ # Note: Original test had "SASL Bind Failed" (capital F), checking both
|
||||
+ assert "SASL Bind" in result.stderr_text
|
||||
+ assert "ailed" in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_server_valid_keytab_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, valid server, keytab, valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_keytab_only_with_kerberos(self):
|
||||
+ """Test ipa-join with keytab only using Kerberos."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--keytab={TEST_KEYTAB}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_keytab_bindpw_invalid(self):
|
||||
+ """Test ipa-join with keytab and invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_keytab_bindpw_valid(self):
|
||||
+ """Test ipa-join with keytab and valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_server_invalid_only_with_kerberos(self):
|
||||
+ """Test ipa-join with invalid server only."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_RESOLVE_ERROR
|
||||
+ assert ERR_COULD_NOT_RESOLVE in result.stderr_text
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+
|
||||
+ def test_server_invalid_bindpw_valid(self):
|
||||
+ """Test ipa-join with invalid server and valid OTP."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--bindpw={OTP}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_ROOT_DN_ERROR
|
||||
+ assert ERR_UNABLE_ROOT_DN in result.stderr_text
|
||||
+
|
||||
+ def test_server_invalid_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with invalid server and keytab."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_RESOLVE_ERROR
|
||||
+ assert ERR_COULD_NOT_RESOLVE in result.stderr_text
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+
|
||||
+ def test_server_valid_only_with_kerberos(self):
|
||||
+ """Test ipa-join with valid server only."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={self.master.hostname}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_server_valid_bindpw_invalid(self):
|
||||
+ """Test ipa-join with valid server and invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_server_valid_bindpw_valid(self):
|
||||
+ """Test ipa-join with valid server and valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_server_valid_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with valid server and keytab."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_bindpw_invalid_only(self):
|
||||
+ """Test ipa-join with invalid bindpw only."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ # =========================================================================
|
||||
+ # OTP (One-Time Password) tests
|
||||
+ # =========================================================================
|
||||
+
|
||||
+ def test_otp_empty_password(self):
|
||||
+ """Test ipa-join with empty OTP password (ipa_otp_1001)."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ '--bindpw=',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_ROOT_DN_ERROR
|
||||
+ assert ERR_UNAUTHENTICATED_BIND in result.stderr_text
|
||||
+
|
||||
+ def test_otp_wrong_password(self):
|
||||
+ """Test ipa-join with wrong OTP password (ipa_otp_1002)."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_otp_valid_password(self):
|
||||
+ """Test ipa-join with valid OTP password (ipa_otp_1003)."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_otp_reuse_fails(self):
|
||||
+ """Test that reusing the same OTP fails (ipa_otp_1004)."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+ try:
|
||||
+ # First use should succeed
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ # Second use of same OTP should fail
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={OTP}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
From 7f6a2835f0972af5e94b58daf47fa60bfade4279 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Thu, 22 Jan 2026 10:02:11 +0100
|
||||
Subject: [PATCH] fetch_domains: Use case-insensitive comparison for domains
|
||||
names
|
||||
|
||||
The fetch_domains method is using netr_DsRGetForestTrustInformation
|
||||
to retrieve the forest trust information. The returned data contains
|
||||
domain entries, with a DNS domain name that can contain mixed case
|
||||
(for instance adDomain.Test).
|
||||
The method compares the domain name with the provided parameter in a
|
||||
case sensitive comparison, while it should use a case-insensitive
|
||||
method (DNS names are case-insensitive).
|
||||
|
||||
Fix the method and compare the lowercase value instead.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9924
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipaserver/dcerpc.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
|
||||
index 1182f128b4988bc699fe7a40d4834f1bead82cf5..5c05ffedb889e774e342cb6cb85ff954d06ac5e9 100644
|
||||
--- a/ipaserver/dcerpc.py
|
||||
+++ b/ipaserver/dcerpc.py
|
||||
@@ -1635,7 +1635,7 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
t.forest_trust_data.netbios_domain_name.string
|
||||
|
||||
tname = unicode(t.forest_trust_data.dns_domain_name.string)
|
||||
- if tname != trustdomain:
|
||||
+ if tname.lower() != trustdomain.lower():
|
||||
result['domains'][tname] = {
|
||||
'cn': tname,
|
||||
'ipantflatname': unicode(
|
||||
@@ -1647,7 +1647,7 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
record.data.string = t.forest_trust_data.string
|
||||
|
||||
tname = unicode(t.forest_trust_data.string)
|
||||
- if tname == trustdomain:
|
||||
+ if tname.lower() == trustdomain.lower():
|
||||
continue
|
||||
|
||||
result['suffixes'][tname] = {'cn': tname}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
43
0010-Handle-IPACertificate-types-in-xmlrpc.patch
Normal file
43
0010-Handle-IPACertificate-types-in-xmlrpc.patch
Normal file
@ -0,0 +1,43 @@
|
||||
From 8deb4be0962b25dfd43e1245307a8bb9d58cfc48 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Tue, 3 Feb 2026 09:46:25 -0500
|
||||
Subject: [PATCH] Handle IPACertificate types in xmlrpc
|
||||
|
||||
The wrapping code didn't understand the IPACertificate class
|
||||
so retrieving any entry that contained one would fail.
|
||||
|
||||
Treat it the same was as its parent class cryptography.Certificate.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9935
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipalib/rpc.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
|
||||
index ed35afc965308e03269f05e01400660b207b548d..9773626eb054dd404256267c5fffbba1aa0579dd 100644
|
||||
--- a/ipalib/rpc.py
|
||||
+++ b/ipalib/rpc.py
|
||||
@@ -56,7 +56,7 @@ from ipalib.errors import (errors_by_code, UnknownError, NetworkError,
|
||||
XMLRPCMarshallError, JSONError)
|
||||
from ipalib import errors, capabilities
|
||||
from ipalib.request import context, Connection
|
||||
-from ipalib.x509 import Encoding as x509_Encoding
|
||||
+from ipalib.x509 import Encoding as x509_Encoding, IPACertificate
|
||||
from ipapython import ipautil
|
||||
from ipapython import session_storage
|
||||
from ipapython.cookie import Cookie
|
||||
@@ -220,7 +220,7 @@ def xml_wrap(value, version):
|
||||
if isinstance(value, Principal):
|
||||
return unicode(value)
|
||||
|
||||
- if isinstance(value, crypto_x509.Certificate):
|
||||
+ if isinstance(value, (crypto_x509.Certificate, IPACertificate)):
|
||||
return base64.b64encode(
|
||||
value.public_bytes(x509_Encoding.DER)).decode('ascii')
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
159
0011-Replace-None-with-when-uninstalling-CA.patch
Normal file
159
0011-Replace-None-with-when-uninstalling-CA.patch
Normal file
@ -0,0 +1,159 @@
|
||||
From a583b0dc08536a50e10b76e27861864b61906355 Mon Sep 17 00:00:00 2001
|
||||
From: David Hanina <dhanina@redhat.com>
|
||||
Date: Mon, 2 Feb 2026 11:14:48 +0100
|
||||
Subject: [PATCH] Replace None with '' when uninstalling CA
|
||||
|
||||
At many places we're obtaining records from a config file and expect the
|
||||
config to have those keys, but the user may delete the line instead of
|
||||
setting the value to false, this then leads to failed uninstall. This
|
||||
patch replaces None with '' at some places that do not check for None.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9921
|
||||
Signed-off-by: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/ca.py | 12 ++++--
|
||||
ipaserver/install/cainstance.py | 14 ++++++-
|
||||
ipaserver/install/server/upgrade.py | 3 +-
|
||||
.../test_integration/test_crlgen_manage.py | 3 +-
|
||||
.../test_integration/test_uninstallation.py | 41 +++++++++++++++++++
|
||||
5 files changed, 66 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
|
||||
index 5a026aa4c556f7012552052cd08223746f3c39ae..2e953a567a3a230cb2a5e35192af76c61f8c1047 100644
|
||||
--- a/ipaserver/install/ca.py
|
||||
+++ b/ipaserver/install/ca.py
|
||||
@@ -340,14 +340,20 @@ def uninstall_crl_check(options):
|
||||
|
||||
try:
|
||||
crlgen_enabled = ca.is_crlgen_enabled()
|
||||
- except cainstance.InconsistentCRLGenConfigException:
|
||||
+ except cainstance.InconsistentCRLGenConfigException as e:
|
||||
# If config is inconsistent, let's be safe and act as if
|
||||
# crl gen was enabled
|
||||
+ print(e)
|
||||
crlgen_enabled = True
|
||||
|
||||
if crlgen_enabled:
|
||||
- print("Deleting this server will leave your installation "
|
||||
- "without a CRL generation master.")
|
||||
+ if not options.ignore_last_of_role:
|
||||
+ print("Deleting this server will leave your installation "
|
||||
+ "without a CRL generation master. Use --ignore-last-of-role "
|
||||
+ "to bypass this check.")
|
||||
+ else:
|
||||
+ print("Deleting this server will leave your installation "
|
||||
+ "without a CRL generation master.")
|
||||
if (options.unattended and not options.ignore_last_of_role) or \
|
||||
not (options.unattended or ipautil.user_input(
|
||||
"Are you sure you want to continue with the uninstall "
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index b8267a625554f9375d27160f39b67ee2e64a2dbb..4933ad23d7323859af92bd02f6ae156803e29997 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -1421,12 +1421,22 @@ class CAInstance(DogtagInstance):
|
||||
try:
|
||||
cache = directivesetter.get_directive(
|
||||
self.config, 'ca.crl.MasterCRL.enableCRLCache', '=')
|
||||
+
|
||||
+ if cache is None:
|
||||
+ raise InconsistentCRLGenConfigException(
|
||||
+ "Configuration is inconsistent, please check "
|
||||
+ "ca.crl.MasterCRL.enableCRLCache, "
|
||||
+ "ca.crl.MasterCRL.enableCRLUpdates and "
|
||||
+ "ca.listenToCloneModifications in {} and "
|
||||
+ "run ipa-crlgen-manage [enable|disable] to repair".format(
|
||||
+ self.config))
|
||||
+
|
||||
enableCRLCache = cache.lower() == 'true'
|
||||
updates = directivesetter.get_directive(
|
||||
- self.config, 'ca.crl.MasterCRL.enableCRLUpdates', '=')
|
||||
+ self.config, 'ca.crl.MasterCRL.enableCRLUpdates', '=') or ''
|
||||
enableCRLUpdates = updates.lower() == 'true'
|
||||
listen = directivesetter.get_directive(
|
||||
- self.config, 'ca.listenToCloneModifications', '=')
|
||||
+ self.config, 'ca.listenToCloneModifications', '=') or ''
|
||||
enableToClone = listen.lower() == 'true'
|
||||
updateinterval = directivesetter.get_directive(
|
||||
self.config, 'ca.certStatusUpdateInterval', '=')
|
||||
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
|
||||
index 548ee02e1e8524ce0002dca1764d48728eb0509a..8692c983409426193e1746f07fa1a0514621cb4a 100644
|
||||
--- a/ipaserver/install/server/upgrade.py
|
||||
+++ b/ipaserver/install/server/upgrade.py
|
||||
@@ -1712,7 +1712,8 @@ def upgrade_configuration():
|
||||
if ca.is_configured():
|
||||
crl = directivesetter.get_directive(
|
||||
paths.CA_CS_CFG_PATH, 'ca.crl.MasterCRL.enableCRLUpdates', '=')
|
||||
- sub_dict['CLONE']='#' if crl.lower() == 'true' else ''
|
||||
+ sub_dict['CLONE'] = '#' if crl is not None and \
|
||||
+ crl.lower() == 'true' else ''
|
||||
|
||||
ds_dirname = dsinstance.config_dirname(ds.serverid)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_crlgen_manage.py b/ipatests/test_integration/test_crlgen_manage.py
|
||||
index c6f41ebf8939bad8006b1e5eaf37bad30dbfd9d8..8a2a28a75b76158fcc61a8e7612f81343336b64f 100644
|
||||
--- a/ipatests/test_integration/test_crlgen_manage.py
|
||||
+++ b/ipatests/test_integration/test_crlgen_manage.py
|
||||
@@ -302,7 +302,8 @@ class TestCRLGenManage(IntegrationTest):
|
||||
['ipa-server-install', '--uninstall', '-U'], raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
expected_msg = "Deleting this server will leave your installation " \
|
||||
- "without a CRL generation master"
|
||||
+ "without a CRL generation master. Use " \
|
||||
+ "--ignore-last-of-role to bypass this check."
|
||||
assert expected_msg in result.stdout_text
|
||||
|
||||
def test_uninstall_with_ignore_last_of_role(self):
|
||||
diff --git a/ipatests/test_integration/test_uninstallation.py b/ipatests/test_integration/test_uninstallation.py
|
||||
index 8d83f72868f5c103b0c31d2aa96630c00b2dfbd8..12b10caa60745dcbc2d811bff65d57fb5d865f09 100644
|
||||
--- a/ipatests/test_integration/test_uninstallation.py
|
||||
+++ b/ipatests/test_integration/test_uninstallation.py
|
||||
@@ -237,3 +237,44 @@ class TestUninstallReinstall(IntegrationTest):
|
||||
|
||||
def test_reinstall_server(self):
|
||||
tasks.install_master(self.master, setup_dns=False)
|
||||
+
|
||||
+
|
||||
+class TestUninstallCRLGen(IntegrationTest):
|
||||
+ """Test uninstallation of a replica with broken CRL configuration.
|
||||
+
|
||||
+ Removing ca.crl.MasterCRL.enableCRLCache from CS.cfg crashed.
|
||||
+ https://pagure.io/freeipa/issue/9921
|
||||
+ """
|
||||
+
|
||||
+ num_replicas = 1
|
||||
+ topology = 'line'
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ tasks.install_master(cls.master, setup_dns=False)
|
||||
+ tasks.install_replica(cls.master, cls.replicas[0])
|
||||
+
|
||||
+ def test_uninstall_replica_with_broken_crlgen(self):
|
||||
+ self.replicas[0].run_command(['ipa-crlgen-manage', 'enable'])
|
||||
+
|
||||
+ self.replicas[0].run_command([
|
||||
+ '/bin/sed',
|
||||
+ '-i',
|
||||
+ '/ca.crl.MasterCRL.enableCRLCache=true/d',
|
||||
+ paths.CA_CS_CFG_PATH,
|
||||
+ ])
|
||||
+
|
||||
+ result = self.replicas[0].run_command([
|
||||
+ 'ipa-server-install',
|
||||
+ '--ignore-last-of-role',
|
||||
+ '--uninstall', '-U',
|
||||
+ ])
|
||||
+
|
||||
+ expected_msg = "Configuration is inconsistent, please check " \
|
||||
+ "ca.crl.MasterCRL.enableCRLCache, " \
|
||||
+ "ca.crl.MasterCRL.enableCRLUpdates and " \
|
||||
+ "ca.listenToCloneModifications in {} and run " \
|
||||
+ "ipa-crlgen-manage [enable|disable] to repair".format(
|
||||
+ paths.CA_CS_CFG_PATH)
|
||||
+
|
||||
+ assert expected_msg in result.stdout_text
|
||||
--
|
||||
2.52.0
|
||||
|
||||
416
0012-ipatests-Add-xmlrpc-tests-for-ipa-delegation-cli.patch
Normal file
416
0012-ipatests-Add-xmlrpc-tests-for-ipa-delegation-cli.patch
Normal file
@ -0,0 +1,416 @@
|
||||
From 91a6618e51b0e767c5cc5e4b1719531dbbd7268d Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Thu, 22 Jan 2026 12:43:56 +0530
|
||||
Subject: [PATCH] ipatests: Add xmlrpc tests for ipa-delegation-cli
|
||||
|
||||
This patch adds below test cases to the the XML-RPC delegation plugin test suite
|
||||
coverage of delegation operations and important bug regressions.
|
||||
|
||||
Test cases added:
|
||||
|
||||
Test basic delegation creation with write permission
|
||||
Test delegation creation with --all flag
|
||||
Test delegation creation with --raw flag (ACI format)
|
||||
Test deletion of delegation with ipausers group
|
||||
Test finding delegation by name criteria
|
||||
Test finding delegation by membergroup filter
|
||||
Test showing delegation by name
|
||||
Test modifying delegation attrs
|
||||
Test modifying delegation permissions
|
||||
BZ 783548: Verify mod fails when membergroup doesn't exist
|
||||
BZ 783554: Verify mod with empty attrs fails properly
|
||||
BZ 888524: Verify find --group option works correctly
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9931
|
||||
Assisted-by: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Anuja More <amore@redhat.com>
|
||||
---
|
||||
.../test_xmlrpc/test_delegation_plugin.py | 371 ++++++++++++++++++
|
||||
1 file changed, 371 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_xmlrpc/test_delegation_plugin.py b/ipatests/test_xmlrpc/test_delegation_plugin.py
|
||||
index b3d2aadbddbaaff6f40e1046e4df32bcc9ee7e2d..9245f259e21cad166c3c5b0565da3bb56a341e6b 100644
|
||||
--- a/ipatests/test_xmlrpc/test_delegation_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_delegation_plugin.py
|
||||
@@ -333,4 +333,375 @@ class test_delegation(Declarative):
|
||||
summary=u'Deleted delegation "%s"' % delegation1,
|
||||
)
|
||||
),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation with mobile attr and write permission',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'test_mobile_delegation'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'editors',
|
||||
+ memberof=u'admins',
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'test_mobile_delegation',
|
||||
+ summary=u'Added delegation "test_mobile_delegation"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'test_mobile_delegation',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation with --all flag',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'test_all_flag'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'editors',
|
||||
+ memberof=u'admins',
|
||||
+ all=True,
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'test_all_flag',
|
||||
+ summary=u'Added delegation "test_all_flag"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'test_all_flag',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation with --raw flag',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'test_raw_flag'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'editors',
|
||||
+ memberof=u'admins',
|
||||
+ raw=True,
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'test_raw_flag',
|
||||
+ summary=u'Added delegation "test_raw_flag"',
|
||||
+ result={
|
||||
+ 'aci': u'(targetattr = "mobile")(targetfilter = '
|
||||
+ u'"(memberOf=%s)")(version 3.0;acl '
|
||||
+ u'"delegation:test_raw_flag";allow (write) '
|
||||
+ u'groupdn = "ldap:///%s";)' % (
|
||||
+ DN(('cn', 'admins'), ('cn', 'groups'),
|
||||
+ ('cn', 'accounts'), api.env.basedn),
|
||||
+ DN(('cn', 'editors'), ('cn', 'groups'),
|
||||
+ ('cn', 'accounts'), api.env.basedn))
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete test_mobile_delegation',
|
||||
+ command=('delegation_del', [u'test_mobile_delegation'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'test_mobile_delegation',
|
||||
+ summary=u'Deleted delegation "test_mobile_delegation"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete test_all_flag',
|
||||
+ command=('delegation_del', [u'test_all_flag'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'test_all_flag',
|
||||
+ summary=u'Deleted delegation "test_all_flag"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete test_raw_flag',
|
||||
+ command=('delegation_del', [u'test_raw_flag'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'test_raw_flag',
|
||||
+ summary=u'Deleted delegation "test_raw_flag"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation for ipausers group',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'delegation_del_positive_1001'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ group=u'ipausers',
|
||||
+ memberof=u'admins',
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_del_positive_1001',
|
||||
+ summary=u'Added delegation "delegation_del_positive_1001"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_del_positive_1001',
|
||||
+ group=u'ipausers',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete delegation_del_positive_1001',
|
||||
+ command=('delegation_del', [u'delegation_del_positive_1001'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'delegation_del_positive_1001',
|
||||
+ summary=u'Deleted delegation "delegation_del_positive_1001"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation for find, show, and mod tests',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'delegation_find_show_mod_test'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'editors',
|
||||
+ memberof=u'admins',
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=u'Added delegation "delegation_find_show_mod_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_find_show_mod_test',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Find delegation by name',
|
||||
+ command=('delegation_find', [u'delegation_find_show_mod_test'], {}),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary=u'1 delegation matched',
|
||||
+ result=[
|
||||
+ {
|
||||
+ 'attrs': [u'mobile'],
|
||||
+ 'permissions': [u'write'],
|
||||
+ 'aciname': u'delegation_find_show_mod_test',
|
||||
+ 'group': u'editors',
|
||||
+ 'memberof': member1,
|
||||
+ },
|
||||
+ ],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Find delegation by membergroup',
|
||||
+ command=('delegation_find', [], {'memberof': member1}),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary=u'1 delegation matched',
|
||||
+ result=[
|
||||
+ {
|
||||
+ 'attrs': [u'mobile'],
|
||||
+ 'permissions': [u'write'],
|
||||
+ 'aciname': u'delegation_find_show_mod_test',
|
||||
+ 'group': u'editors',
|
||||
+ 'memberof': member1,
|
||||
+ },
|
||||
+ ],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Show delegation by name',
|
||||
+ command=('delegation_show', [u'delegation_find_show_mod_test'], {}),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=None,
|
||||
+ result={
|
||||
+ 'attrs': [u'mobile'],
|
||||
+ 'permissions': [u'write'],
|
||||
+ 'aciname': u'delegation_find_show_mod_test',
|
||||
+ 'group': u'editors',
|
||||
+ 'memberof': member1,
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Modify delegation attrs',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_find_show_mod_test'],
|
||||
+ dict(attrs=[u'l'])
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=u'Modified delegation "delegation_find_show_mod_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'l'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_find_show_mod_test',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Modify delegation permissions',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_find_show_mod_test'],
|
||||
+ dict(permissions=u'read')
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=u'Modified delegation "delegation_find_show_mod_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'l'],
|
||||
+ permissions=[u'read'],
|
||||
+ aciname=u'delegation_find_show_mod_test',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete delegation_find_show_mod_test',
|
||||
+ command=('delegation_del', [u'delegation_find_show_mod_test'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=u'Deleted delegation "delegation_find_show_mod_test"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation for BZ tests',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'delegation_bz_test'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'ipausers',
|
||||
+ memberof=u'admins',
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_bz_test',
|
||||
+ summary=u'Added delegation "delegation_bz_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_bz_test',
|
||||
+ group=u'ipausers',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Try to modify with non-existent membergroup (BZ 783548)',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_bz_test'],
|
||||
+ dict(memberof=u'badmembergroup')
|
||||
+ ),
|
||||
+ expected=errors.NotFound(
|
||||
+ reason=u'badmembergroup: group not found'),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Try to modify attrs with empty value (BZ 783554)',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_bz_test'], dict(attrs=u'')
|
||||
+ ),
|
||||
+ expected=errors.RequirementError(name='attrs'),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Modify attrs to prepare for next BZ test',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_bz_test'], dict(attrs=[u'l'])
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_bz_test',
|
||||
+ summary=u'Modified delegation "delegation_bz_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'l'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_bz_test',
|
||||
+ group=u'ipausers',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Find delegation by group filter (BZ 888524)',
|
||||
+ command=('delegation_find', [], {'group': u'ipausers'}),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary=u'1 delegation matched',
|
||||
+ result=[
|
||||
+ {
|
||||
+ 'attrs': [u'l'],
|
||||
+ 'permissions': [u'write'],
|
||||
+ 'aciname': u'delegation_bz_test',
|
||||
+ 'group': u'ipausers',
|
||||
+ 'memberof': member1,
|
||||
+ },
|
||||
+ ],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete delegation_bz_test',
|
||||
+ command=('delegation_del', [u'delegation_bz_test'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'delegation_bz_test',
|
||||
+ summary=u'Deleted delegation "delegation_bz_test"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
]
|
||||
--
|
||||
2.52.0
|
||||
|
||||
49
0013-ipa-join-initialize-pointer.patch
Normal file
49
0013-ipa-join-initialize-pointer.patch
Normal file
@ -0,0 +1,49 @@
|
||||
From 7cc96e42683a6d3ec9f2dc2a19e99330b6f3ce58 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Wed, 4 Feb 2026 09:21:14 +0100
|
||||
Subject: [PATCH] ipa-join: initialize pointer
|
||||
|
||||
OpenScanHub detected an uninitialized pointer in ipa_join:
|
||||
Slapi_DN *sdn;
|
||||
...
|
||||
if (sdn) slapi_sdn_free(&sdn);
|
||||
|
||||
Initialize to NULL
|
||||
Also initialize Slapi_Backend *be=NULL and char * filter=NULL
|
||||
to avoid potential issues.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9936
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
index 3a70dd0a5594fc623e7e808ab8a734349a748a49..2f8923e10310a8a6e19ac701070d6451915c3be3 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
@@ -129,8 +129,8 @@ ipa_join(Slapi_PBlock *pb)
|
||||
Slapi_PBlock *pbte = NULL;
|
||||
Slapi_PBlock *pbtm = NULL;
|
||||
Slapi_Entry *targetEntry=NULL;
|
||||
- Slapi_DN *sdn;
|
||||
- Slapi_Backend *be;
|
||||
+ Slapi_DN *sdn=NULL;
|
||||
+ Slapi_Backend *be=NULL;
|
||||
Slapi_Entry **es = NULL;
|
||||
int rc=0, ret=0, res;
|
||||
size_t i;
|
||||
@@ -139,7 +139,7 @@ ipa_join(Slapi_PBlock *pb)
|
||||
char *fqdn = NULL;
|
||||
Slapi_Mods *smods = NULL;
|
||||
char *attrlist[] = {"fqdn", "krbPrincipalKey", "krbLastPwdChange", "krbPrincipalName", NULL };
|
||||
- char * filter;
|
||||
+ char * filter=NULL;
|
||||
|
||||
int scope = LDAP_SCOPE_SUBTREE;
|
||||
char *principal = NULL;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
39
freeipa.spec
39
freeipa.spec
@ -234,7 +234,7 @@
|
||||
|
||||
Name: %{package_name}
|
||||
Version: %{IPA_VERSION}
|
||||
Release: 1%{?rc_version:.%rc_version}%{?dist}
|
||||
Release: 2%{?rc_version:.%rc_version}%{?dist}
|
||||
Summary: The Identity, Policy and Audit system
|
||||
|
||||
License: GPL-3.0-or-later
|
||||
@ -262,6 +262,19 @@ Source2: gpgkey-0E63D716D76AC080A4A33513F40800B6298EB963.asc
|
||||
# RHEL spec file only: START
|
||||
%if %{NON_DEVELOPER_BUILD}
|
||||
%if 0%{?rhel} >= 9
|
||||
Patch0001: 0001-ipatests-Move-expire_password-fixture-into-TestIPACo.patch
|
||||
Patch0002: 0002-ipatests-Fix-xfail-assertion-for-sssd-2.12.0.patch
|
||||
Patch0003: 0003-ipatests-Add-DNS-functional-integration-tests.patch
|
||||
Patch0004: 0004-ipa-advise-smart-card-client-script-does-not-need-kr.patch
|
||||
Patch0005: 0005-ipatests-Fix-resolver-state-tracking-in-enforced-DNS.patch
|
||||
Patch0006: 0006-freeipa.spec.in-Use-systemd-sysusers-to-setup-users-.patch
|
||||
Patch0007: 0007-ipatests-add-Random-Password-based-replica-promotion.patch
|
||||
Patch0008: 0008-ipatests-Add-integration-tests-for-ipa-join-command.patch
|
||||
Patch0009: 0009-fetch_domains-Use-case-insensitive-comparison-for-do.patch
|
||||
Patch0010: 0010-Handle-IPACertificate-types-in-xmlrpc.patch
|
||||
Patch0011: 0011-Replace-None-with-when-uninstalling-CA.patch
|
||||
Patch0012: 0012-ipatests-Add-xmlrpc-tests-for-ipa-delegation-cli.patch
|
||||
Patch0013: 0013-ipa-join-initialize-pointer.patch
|
||||
Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch
|
||||
%endif
|
||||
%endif
|
||||
@ -601,7 +614,7 @@ Requires: systemd-units >= %{systemd_version}
|
||||
Requires: system-logos-ipa >= 80.4
|
||||
%endif
|
||||
|
||||
# The list below is automatically generated by `fix-spec.sh -i`
|
||||
# The list below is automatically generated by `fix-spec.sh -i`
|
||||
# from the install/freeipa-webui
|
||||
Provides: bundled(npm(attr-accept)) = 2.2.5
|
||||
Provides: bundled(npm(cookie)) = 1.0.2
|
||||
@ -1247,6 +1260,7 @@ fi
|
||||
/bin/systemctl reload-or-try-restart dbus
|
||||
/bin/systemctl reload-or-try-restart oddjobd
|
||||
|
||||
%sysusers_create %{_sysusersdir}/ipa.conf
|
||||
%tmpfiles_create ipa.conf
|
||||
%journal_catalog_update
|
||||
|
||||
@ -1304,18 +1318,6 @@ if [ -e /usr/sbin/ipa_kpasswd ]; then
|
||||
fi
|
||||
|
||||
|
||||
%pre server-common
|
||||
# create users and groups
|
||||
# create kdcproxy group and user
|
||||
getent group kdcproxy >/dev/null || groupadd -f -r kdcproxy
|
||||
getent passwd kdcproxy >/dev/null || useradd -r -g kdcproxy -s /sbin/nologin -d / -c "IPA KDC Proxy User" kdcproxy
|
||||
# create ipaapi group and user
|
||||
getent group ipaapi >/dev/null || groupadd -f -r ipaapi
|
||||
getent passwd ipaapi >/dev/null || useradd -r -g ipaapi -s /sbin/nologin -d / -c "IPA Framework User" ipaapi
|
||||
# add apache to ipaaapi group
|
||||
id -Gn apache | grep '\bipaapi\b' >/dev/null || usermod apache -a -G ipaapi
|
||||
|
||||
|
||||
%post server-dns
|
||||
%systemd_post ipa-dnskeysyncd.service ipa-ods-exporter.socket ipa-ods-exporter.service
|
||||
|
||||
@ -1702,6 +1704,7 @@ fi
|
||||
%dir %attr(0755,root,root) %{_sysconfdir}/ipa/kdcproxy
|
||||
%config(noreplace) %{_sysconfdir}/ipa/kdcproxy/kdcproxy.conf
|
||||
# NOTE: systemd specific section
|
||||
%{_sysusersdir}/ipa.conf
|
||||
%{_tmpfilesdir}/ipa.conf
|
||||
%attr(644,root,root) %{_unitdir}/ipa-custodia.service
|
||||
%ghost %attr(644,root,root) %{etc_systemd_dir}/httpd.d/ipa.conf
|
||||
@ -1979,6 +1982,14 @@ fi
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Fri Feb 06 2026 Florence Blanc-Renaud <flo@redhat.com> - 4.13.1-2
|
||||
- Resolves: RHEL-146023 When using xmlrpc, ipa server failed with assert type(value) in (unicode, float, int, bool, type(None))
|
||||
-Resolves: RHEL-145855 Include latest fixes in python3-ipatests package
|
||||
-Resolves: RHEL-88855 ipa uninstallation is failing with message "'NoneType' object has no attribute 'lower'"
|
||||
-Resolves: RHEL-43143 ipa-advise client script requires keytab (should just require root access on client system)
|
||||
-Resolves: RHEL-4895 ipa use systemd-sysusers
|
||||
-Resolves: RHEL-4823 Names of domains from a trusted forest should be compared case-insentive
|
||||
|
||||
* Fri Jan 16 2026 Florence Blanc-Renaud <flo@redhat.com> - 4.13.1-1
|
||||
- Resolves: RHEL-140587 Support replaceable WebUI artwork for RHEL and CentOS
|
||||
- Resolves: RHEL-113778 Command that retrieve and install new CA certificates
|
||||
|
||||
Loading…
Reference in New Issue
Block a user