import CS ipa-4.13.1-3.el9.2
This commit is contained in:
parent
0182647e8f
commit
5c1d6100a2
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
||||
SOURCES/freeipa-4.12.2.tar.gz
|
||||
SOURCES/freeipa-4.13.1.tar.gz
|
||||
|
||||
@ -1 +1 @@
|
||||
e06782e6f1d33fdbdc27011d10737c8d2fb815a8 SOURCES/freeipa-4.12.2.tar.gz
|
||||
bc82618e66b45376464cd2206bf6e7a02f766a11 SOURCES/freeipa-4.13.1.tar.gz
|
||||
|
||||
@ -45,9 +45,9 @@ index 6803de752bc122bf6e1eafd610d399cde994cad5..a532fe85c34a523519187b6fd1297c19
|
||||
Requires: python3-netaddr >= %{python_netaddr_version}
|
||||
-Requires: python3-ifaddr
|
||||
+Requires: python3-netifaces >= 0.10.4
|
||||
Requires: python3-packaging
|
||||
Requires: python3-pyasn1 >= 0.3.2-2
|
||||
Requires: python3-pyasn1-modules >= 0.3.2-2
|
||||
Requires: python3-pyusb
|
||||
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
|
||||
index 29aff5f413e7f27136e236382031171f068284c5..263dc8e302cccd76412c294e0740af9744a2c62d 100644
|
||||
--- a/ipaclient/install/client.py
|
||||
|
||||
890
SOURCES/0003-Revert-Remove-NIS-server-support.patch
Normal file
890
SOURCES/0003-Revert-Remove-NIS-server-support.patch
Normal file
@ -0,0 +1,890 @@
|
||||
From 941d6a54e163ca0d5eee6ac391c0d2f5afb6cf55 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 9 Dec 2025 10:27:12 +0100
|
||||
Subject: [PATCH] Revert "Remove NIS server support"
|
||||
|
||||
This reverts commit e98a80827bcc42dc16b516355077fed844220107.
|
||||
---
|
||||
freeipa.spec.in | 2 +
|
||||
install/share/Makefile.am | 2 +
|
||||
install/share/nis-update.uldif | 38 ++++
|
||||
install/share/nis.uldif | 96 ++++++++++
|
||||
install/tools/Makefile.am | 2 +
|
||||
install/tools/ipa-compat-manage.in | 17 +-
|
||||
install/tools/ipa-nis-manage.in | 205 +++++++++++++++++++++
|
||||
install/tools/man/Makefile.am | 1 +
|
||||
install/tools/man/ipa-nis-manage.1 | 51 +++++
|
||||
install/updates/10-enable-betxn.update | 3 +
|
||||
install/updates/50-nis.update | 3 +
|
||||
install/updates/Makefile.am | 1 +
|
||||
ipaplatform/base/paths.py | 2 +
|
||||
ipaserver/install/ipa_migrate.py | 27 ++-
|
||||
ipaserver/install/ipa_migrate_constants.py | 24 +++
|
||||
ipaserver/install/plugins/update_nis.py | 92 +++++++++
|
||||
ipatests/test_cmdline/test_cli.py | 1 +
|
||||
ipatests/test_integration/test_commands.py | 87 +++++++++
|
||||
18 files changed, 638 insertions(+), 16 deletions(-)
|
||||
create mode 100644 install/share/nis-update.uldif
|
||||
create mode 100644 install/share/nis.uldif
|
||||
create mode 100644 install/tools/ipa-nis-manage.in
|
||||
create mode 100644 install/tools/man/ipa-nis-manage.1
|
||||
create mode 100644 install/updates/50-nis.update
|
||||
create mode 100644 ipaserver/install/plugins/update_nis.py
|
||||
|
||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||
index b93199a5783a89e0ff58affd3d416556f72dd00c..6ee0f43f059dad4bba2288b1b0d3a63437371861 100644
|
||||
--- a/freeipa.spec.in
|
||||
+++ b/freeipa.spec.in
|
||||
@@ -1619,6 +1619,7 @@ fi
|
||||
%{_sbindir}/ipa-ldap-updater
|
||||
%{_sbindir}/ipa-otptoken-import
|
||||
%{_sbindir}/ipa-compat-manage
|
||||
+%{_sbindir}/ipa-nis-manage
|
||||
%{_sbindir}/ipa-managed-entries
|
||||
%{_sbindir}/ipactl
|
||||
%{_sbindir}/ipa-advise
|
||||
@@ -1694,6 +1695,7 @@ fi
|
||||
%{_mandir}/man1/ipa-ca-install.1*
|
||||
%{_mandir}/man1/ipa-kra-install.1*
|
||||
%{_mandir}/man1/ipa-compat-manage.1*
|
||||
+%{_mandir}/man1/ipa-nis-manage.1*
|
||||
%{_mandir}/man1/ipa-managed-entries.1*
|
||||
%{_mandir}/man1/ipa-ldap-updater.1*
|
||||
%{_mandir}/man8/ipactl.8*
|
||||
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
|
||||
index bb2eb7531b083e36827ba1ee111e39c29589acaa..87578d29644f897622f46a56c06cb24c7df3efe6 100644
|
||||
--- a/install/share/Makefile.am
|
||||
+++ b/install/share/Makefile.am
|
||||
@@ -66,6 +66,8 @@ dist_app_DATA = \
|
||||
master-entry.ldif \
|
||||
memberof-task.ldif \
|
||||
memberof-conf.ldif \
|
||||
+ nis.uldif \
|
||||
+ nis-update.uldif \
|
||||
opendnssec_conf.template \
|
||||
opendnssec_kasp.template \
|
||||
unique-attributes.ldif \
|
||||
diff --git a/install/share/nis-update.uldif b/install/share/nis-update.uldif
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..e602c1de061fbcece349b2d86970c4db5051473b
|
||||
--- /dev/null
|
||||
+++ b/install/share/nis-update.uldif
|
||||
@@ -0,0 +1,38 @@
|
||||
+# Updates for NIS
|
||||
+
|
||||
+# Correct syntax error that caused users to not appear
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
|
||||
+replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
|
||||
+
|
||||
+# Correct syntax error that caused nested netgroups to not work
|
||||
+# https://bugzilla.redhat.com/show_bug.cgi?id=788625
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
|
||||
+replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
|
||||
+
|
||||
+# Make the padding an expression so usercat and hostcat always gets
|
||||
+# evaluated when displaying entries.
|
||||
+# https://bugzilla.redhat.com/show_bug.cgi?id=767372
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
|
||||
+replace:nis-value-format: %merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\"),%{nisDomainName:-})")
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=ethers.byaddr, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: ethers.byaddr
|
||||
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
|
||||
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
|
||||
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6")
|
||||
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=ethers.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: ethers.byname
|
||||
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
|
||||
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
|
||||
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%7")
|
||||
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
|
||||
+default:nis-secure: no
|
||||
diff --git a/install/share/nis.uldif b/install/share/nis.uldif
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1735fb55299d28ab40d864b24062309fca43241f
|
||||
--- /dev/null
|
||||
+++ b/install/share/nis.uldif
|
||||
@@ -0,0 +1,96 @@
|
||||
+dn: cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: nsSlapdPlugin
|
||||
+default:objectclass: extensibleObject
|
||||
+default:cn: NIS Server
|
||||
+default:nsslapd-pluginpath: /usr/lib$LIBARCH/dirsrv/plugins/nisserver-plugin.so
|
||||
+default:nsslapd-plugininitfunc: nis_plugin_init
|
||||
+default:nsslapd-plugintype: object
|
||||
+default:nsslapd-pluginbetxn: on
|
||||
+default:nsslapd-pluginenabled: on
|
||||
+default:nsslapd-pluginid: nis-server
|
||||
+default:nsslapd-pluginversion: 0.10
|
||||
+default:nsslapd-pluginvendor: redhat.com
|
||||
+default:nsslapd-plugindescription: NIS Server Plugin
|
||||
+default:nis-tcp-wrappers-name: nis-server
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=passwd.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: passwd.byname
|
||||
+default:nis-base: cn=users, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=passwd.byuid, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: passwd.byuid
|
||||
+default:nis-base: cn=users, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=group.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: group.byname
|
||||
+default:nis-base: cn=groups, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=group.bygid, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: group.bygid
|
||||
+default:nis-base: cn=groups, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netid.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: netid.byname
|
||||
+default:nis-base: cn=users, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+# Note that the escapes in this entry can be quite confusing. The trick
|
||||
+# is that each level of nesting requires (2^n) - 1 escapes. So the
|
||||
+# first level is \", the second is \\\", the third is \\\\\\\", etc.
|
||||
+# (1, 3, 7, 15, more than that and you'll go insane)
|
||||
+
|
||||
+# Note that this configuration mirrors the Schema Compat configuration for
|
||||
+# triples.
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: netgroup
|
||||
+default:nis-base: cn=ng, cn=alt, $SUFFIX
|
||||
+default:nis-filter: (objectClass=ipanisNetgroup)
|
||||
+default:nis-key-format: %{cn}
|
||||
+default:nis-value-format:%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\"),%{nisDomainName:-})")
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=ethers.byaddr, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: ethers.byaddr
|
||||
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
|
||||
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
|
||||
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6")
|
||||
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=ethers.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: ethers.byname
|
||||
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
|
||||
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
|
||||
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%7")
|
||||
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
|
||||
+default:nis-secure: no
|
||||
+
|
||||
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
|
||||
index 74cd11c67dd3af623dd29802935ae63fe82fcecb..ca484ec37969c9c06ae7b408b55fa30cd4e8e4fe 100644
|
||||
--- a/install/tools/Makefile.am
|
||||
+++ b/install/tools/Makefile.am
|
||||
@@ -19,6 +19,7 @@ dist_noinst_DATA = \
|
||||
ipa-server-upgrade.in \
|
||||
ipactl.in \
|
||||
ipa-compat-manage.in \
|
||||
+ ipa-nis-manage.in \
|
||||
ipa-managed-entries.in \
|
||||
ipa-ldap-updater.in \
|
||||
ipa-otptoken-import.in \
|
||||
@@ -56,6 +57,7 @@ nodist_sbin_SCRIPTS = \
|
||||
ipa-server-upgrade \
|
||||
ipactl \
|
||||
ipa-compat-manage \
|
||||
+ ipa-nis-manage \
|
||||
ipa-managed-entries \
|
||||
ipa-ldap-updater \
|
||||
ipa-otptoken-import \
|
||||
diff --git a/install/tools/ipa-compat-manage.in b/install/tools/ipa-compat-manage.in
|
||||
index fb25c22edd5b2fac123144846ffc232cb5bbecc7..9650abd6f83ebc0a8ef347fee83989d4e9f13f09 100644
|
||||
--- a/install/tools/ipa-compat-manage.in
|
||||
+++ b/install/tools/ipa-compat-manage.in
|
||||
@@ -25,7 +25,6 @@ import sys
|
||||
from ipaplatform.paths import paths
|
||||
try:
|
||||
from ipapython import ipautil, config
|
||||
- from ipapython.ipaldap import realm_to_serverid
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
from ipalib import api, errors
|
||||
@@ -153,19 +152,9 @@ def main():
|
||||
try:
|
||||
entry = get_entry(nis_config_dn)
|
||||
# We can't disable schema compat if the NIS plugin is enabled
|
||||
- if (
|
||||
- entry is not None
|
||||
- and entry.get("nsslapd-pluginenabled", [""])[0].lower() == "on"
|
||||
- ):
|
||||
- instance = realm_to_serverid(api.env.realm)
|
||||
- print(
|
||||
- "The NIS plugin is configured, cannot "
|
||||
- "disable compatibility.", file=sys.stderr,
|
||||
- )
|
||||
- print(
|
||||
- f"Run \"dsconf {instance} plugin set --enabled off "
|
||||
- "'NIS Server'\" first.", file=sys.stderr,
|
||||
- )
|
||||
+ if entry is not None and entry.get('nsslapd-pluginenabled', [''])[0].lower() == 'on':
|
||||
+ print("The NIS plugin is configured, cannot disable compatibility.", file=sys.stderr)
|
||||
+ print("Run 'ipa-nis-manage disable' first.", file=sys.stderr)
|
||||
retval = 2
|
||||
except errors.ExecutionError as lde:
|
||||
print("An error occurred while talking to the server.")
|
||||
diff --git a/install/tools/ipa-nis-manage.in b/install/tools/ipa-nis-manage.in
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6b156ce6a80d05d9cd2d7cab48e6dba08b0954f5
|
||||
--- /dev/null
|
||||
+++ b/install/tools/ipa-nis-manage.in
|
||||
@@ -0,0 +1,205 @@
|
||||
+#!/usr/bin/python3
|
||||
+# Authors: Rob Crittenden <rcritten@redhat.com>
|
||||
+# Authors: Simo Sorce <ssorce@redhat.com>
|
||||
+#
|
||||
+# Copyright (C) 2009 Red Hat
|
||||
+# see file 'COPYING' for use and warranty information
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify
|
||||
+# it under the terms of the GNU General Public License as published by
|
||||
+# the Free Software Foundation, either version 3 of the License, or
|
||||
+# (at your option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful,
|
||||
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+# GNU General Public License for more details.
|
||||
+#
|
||||
+# You should have received a copy of the GNU General Public License
|
||||
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
+#
|
||||
+
|
||||
+from __future__ import print_function
|
||||
+
|
||||
+import sys
|
||||
+import os
|
||||
+from ipaplatform.paths import paths
|
||||
+try:
|
||||
+ from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
+ from ipapython import ipautil, config
|
||||
+ from ipaserver.install import installutils
|
||||
+ from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
+ from ipalib import api, errors
|
||||
+ from ipapython.ipa_log_manager import standard_logging_setup
|
||||
+ from ipapython.dn import DN
|
||||
+ from ipaplatform import services
|
||||
+except ImportError as e:
|
||||
+ print("""\
|
||||
+There was a problem importing one of the required Python modules. The
|
||||
+error was:
|
||||
+
|
||||
+ %s
|
||||
+""" % e, file=sys.stderr)
|
||||
+ sys.exit(1)
|
||||
+
|
||||
+nis_config_dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+compat_dn = DN(('cn', 'Schema Compatibility'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+
|
||||
+def parse_options():
|
||||
+ usage = "%prog [options] <enable|disable|status>\n"
|
||||
+ usage += "%prog [options]\n"
|
||||
+ parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
|
||||
+
|
||||
+ parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
+ help="Display debugging information about the update(s)")
|
||||
+ parser.add_option("-y", dest="password",
|
||||
+ help="File containing the Directory Manager password")
|
||||
+
|
||||
+ config.add_standard_options(parser)
|
||||
+ options, args = parser.parse_args()
|
||||
+
|
||||
+ return options, args
|
||||
+
|
||||
+def get_dirman_password():
|
||||
+ """Prompt the user for the Directory Manager password and verify its
|
||||
+ correctness.
|
||||
+ """
|
||||
+ password = installutils.read_password("Directory Manager", confirm=False, validate=False, retry=False)
|
||||
+
|
||||
+ return password
|
||||
+
|
||||
+def get_entry(dn):
|
||||
+ """
|
||||
+ Return the entry for the given DN. If the entry is not found return
|
||||
+ None.
|
||||
+ """
|
||||
+ entry = None
|
||||
+ try:
|
||||
+ entry = api.Backend.ldap2.get_entry(dn)
|
||||
+ except errors.NotFound:
|
||||
+ pass
|
||||
+ return entry
|
||||
+
|
||||
+def main():
|
||||
+ retval = 0
|
||||
+ files = [paths.NIS_ULDIF]
|
||||
+ servicemsg = ""
|
||||
+
|
||||
+ if os.getegid() != 0:
|
||||
+ sys.exit('Must be root to use this tool.')
|
||||
+
|
||||
+ installutils.check_server_configuration()
|
||||
+
|
||||
+ options, args = parse_options()
|
||||
+
|
||||
+ if len(args) != 1:
|
||||
+ sys.exit("You must specify one action: enable | disable | status")
|
||||
+ elif args[0] not in {"enable", "disable", "status"}:
|
||||
+ sys.exit("Unrecognized action [" + args[0] + "]")
|
||||
+
|
||||
+ standard_logging_setup(None, debug=options.debug)
|
||||
+ dirman_password = ""
|
||||
+ if options.password:
|
||||
+ try:
|
||||
+ pw = ipautil.template_file(options.password, [])
|
||||
+ except IOError:
|
||||
+ sys.exit("File \"%s\" not found or not readable" % options.password)
|
||||
+ dirman_password = pw.strip()
|
||||
+ else:
|
||||
+ dirman_password = get_dirman_password()
|
||||
+ if dirman_password is None:
|
||||
+ sys.exit("Directory Manager password required")
|
||||
+
|
||||
+ if not dirman_password:
|
||||
+ sys.exit("No password supplied")
|
||||
+
|
||||
+ api.bootstrap(
|
||||
+ context='cli', confdir=paths.ETC_IPA,
|
||||
+ debug=options.debug, in_server=True)
|
||||
+ api.finalize()
|
||||
+ api.Backend.ldap2.connect(bind_pw=dirman_password)
|
||||
+
|
||||
+ if args[0] == "enable":
|
||||
+ compat = get_entry(compat_dn)
|
||||
+ if compat is None or compat.get('nsslapd-pluginenabled', [''])[0].lower() == 'off':
|
||||
+ sys.exit("The compat plugin needs to be enabled: ipa-compat-manage enable")
|
||||
+ entry = None
|
||||
+ try:
|
||||
+ entry = get_entry(nis_config_dn)
|
||||
+ except errors.ExecutionError as lde:
|
||||
+ print("An error occurred while talking to the server.")
|
||||
+ print(lde)
|
||||
+ retval = 1
|
||||
+
|
||||
+ # Enable either the portmap or rpcbind service
|
||||
+ portmap = services.knownservices.portmap
|
||||
+ rpcbind = services.knownservices.rpcbind
|
||||
+
|
||||
+ if portmap.is_installed():
|
||||
+ portmap.enable()
|
||||
+ servicemsg = portmap.service_name
|
||||
+ elif rpcbind.is_installed():
|
||||
+ rpcbind.enable()
|
||||
+ servicemsg = rpcbind.service_name
|
||||
+ else:
|
||||
+ print("Unable to enable either %s or %s" % (portmap.service_name, rpcbind.service_name))
|
||||
+ retval = 3
|
||||
+
|
||||
+ # The cn=config entry for the plugin may already exist but it
|
||||
+ # could be turned off, handle both cases.
|
||||
+ if entry is None:
|
||||
+ print("Enabling plugin")
|
||||
+ ld = LDAPUpdate()
|
||||
+ if ld.update(files) != True:
|
||||
+ retval = 1
|
||||
+ elif entry.get('nsslapd-pluginenabled', [''])[0].lower() == 'off':
|
||||
+ print("Enabling plugin")
|
||||
+ # Already configured, just enable the plugin
|
||||
+ entry['nsslapd-pluginenabled'] = ['on']
|
||||
+ api.Backend.ldap2.update_entry(entry)
|
||||
+ else:
|
||||
+ print("Plugin already Enabled")
|
||||
+ retval = 2
|
||||
+
|
||||
+ elif args[0] == "disable":
|
||||
+ try:
|
||||
+ entry = api.Backend.ldap2.get_entry(nis_config_dn, ['nsslapd-pluginenabled'])
|
||||
+ entry['nsslapd-pluginenabled'] = ['off']
|
||||
+ api.Backend.ldap2.update_entry(entry)
|
||||
+ except (errors.NotFound, errors.EmptyModlist):
|
||||
+ print("Plugin is already disabled")
|
||||
+ retval = 2
|
||||
+ except errors.LDAPError as lde:
|
||||
+ print("An error occurred while talking to the server.")
|
||||
+ print(lde)
|
||||
+ retval = 1
|
||||
+
|
||||
+ elif args[0] == "status":
|
||||
+ nis_entry = get_entry(nis_config_dn)
|
||||
+ enabled = (nis_entry and
|
||||
+ nis_entry.get(
|
||||
+ 'nsslapd-pluginenabled', '')[0].lower() == "on")
|
||||
+ if enabled:
|
||||
+ print("Plugin is enabled")
|
||||
+ retval = 0
|
||||
+ else:
|
||||
+ print("Plugin is not enabled")
|
||||
+ retval = 4
|
||||
+
|
||||
+ else:
|
||||
+ retval = 1
|
||||
+
|
||||
+ if retval == 0:
|
||||
+ if args[0] in {"enable", "disable"}:
|
||||
+ print("This setting will not take effect until you restart "
|
||||
+ "Directory Server.")
|
||||
+
|
||||
+ if args[0] == "enable":
|
||||
+ print("The %s service may need to be started." % servicemsg)
|
||||
+
|
||||
+ api.Backend.ldap2.disconnect()
|
||||
+
|
||||
+ return retval
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ installutils.run_script(main, operation_name='ipa-nis-manage')
|
||||
diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
|
||||
index bffce402c7df428a47c1710bdc47d59a95993a0d..e9542a77bbbb88054eae1e64311d6e9ec5bee499 100644
|
||||
--- a/install/tools/man/Makefile.am
|
||||
+++ b/install/tools/man/Makefile.am
|
||||
@@ -18,6 +18,7 @@ dist_man1_MANS = \
|
||||
ipa-kra-install.1 \
|
||||
ipa-ldap-updater.1 \
|
||||
ipa-compat-manage.1 \
|
||||
+ ipa-nis-manage.1 \
|
||||
ipa-managed-entries.1 \
|
||||
ipa-backup.1 \
|
||||
ipa-restore.1 \
|
||||
diff --git a/install/tools/man/ipa-nis-manage.1 b/install/tools/man/ipa-nis-manage.1
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1107b7790531aa695defd660040734143c02474d
|
||||
--- /dev/null
|
||||
+++ b/install/tools/man/ipa-nis-manage.1
|
||||
@@ -0,0 +1,51 @@
|
||||
+.\" A man page for ipa-nis-manage
|
||||
+.\" Copyright (C) 2009 Red Hat, Inc.
|
||||
+.\"
|
||||
+.\" This program is free software; you can redistribute it and/or modify
|
||||
+.\" it under the terms of the GNU General Public License as published by
|
||||
+.\" the Free Software Foundation, either version 3 of the License, or
|
||||
+.\" (at your option) any later version.
|
||||
+.\"
|
||||
+.\" This program is distributed in the hope that it will be useful, but
|
||||
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+.\" General Public License for more details.
|
||||
+.\"
|
||||
+.\" You should have received a copy of the GNU General Public License
|
||||
+.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
+.\"
|
||||
+.\" Author: Rob Crittenden <rcritten@redhat.com>
|
||||
+.\"
|
||||
+.TH "ipa-nis-manage" "1" "April 25 2016" "IPA" "IPA Manual Pages"
|
||||
+.SH "NAME"
|
||||
+ipa\-nis\-manage \- Enables or disables the NIS listener plugin
|
||||
+.SH "SYNOPSIS"
|
||||
+ipa\-nis\-manage [options] <enable|disable|status>
|
||||
+.SH "DESCRIPTION"
|
||||
+Run the command with the \fBenable\fR option to enable the NIS plugin.
|
||||
+
|
||||
+Run the command with the \fBdisable\fR option to disable the NIS plugin.
|
||||
+
|
||||
+Run the command with the \fBstatus\fR option to read status of the NIS plugin. Return code 0 indicates enabled plugin, return code 4 indicates disabled plugin.
|
||||
+
|
||||
+In all cases the user will be prompted to provide the Directory Manager's password unless option \fB\-y\fR is used.
|
||||
+
|
||||
+Directory Server will need to be restarted after the NIS listener plugin has been enabled.
|
||||
+
|
||||
+.SH "OPTIONS"
|
||||
+.TP
|
||||
+\fB\-d\fR, \fB\-\-debug\fR
|
||||
+Enable debug logging when more verbose output is needed
|
||||
+.TP
|
||||
+\fB\-y\fR \fIfile\fR
|
||||
+File containing the Directory Manager password
|
||||
+.SH "EXIT STATUS"
|
||||
+0 if the command was successful
|
||||
+
|
||||
+1 if an error occurred
|
||||
+
|
||||
+2 if the plugin is already in the required status (enabled or disabled)
|
||||
+
|
||||
+3 if RPC services cannot be enabled.
|
||||
+
|
||||
+4 if status command detected plugin in disabled state.
|
||||
diff --git a/install/updates/10-enable-betxn.update b/install/updates/10-enable-betxn.update
|
||||
index 9525292cb2d66a738a2dec9cd881dbf960d2d944..1f89341c7f68616161d03b8a2e0c34f499bc3317 100644
|
||||
--- a/install/updates/10-enable-betxn.update
|
||||
+++ b/install/updates/10-enable-betxn.update
|
||||
@@ -44,3 +44,6 @@ only: nsslapd-pluginbetxn: on
|
||||
|
||||
dn: cn=Schema Compatibility, cn=plugins, cn=config
|
||||
onlyifexist: nsslapd-pluginbetxn: on
|
||||
+
|
||||
+dn: cn=NIS Server, cn=plugins, cn=config
|
||||
+onlyifexist: nsslapd-pluginbetxn: on
|
||||
diff --git a/install/updates/50-nis.update b/install/updates/50-nis.update
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..05a166f003aefc50fc25f10f01f7364d752425bc
|
||||
--- /dev/null
|
||||
+++ b/install/updates/50-nis.update
|
||||
@@ -0,0 +1,3 @@
|
||||
+# Updates are applied only if NIS plugin has been configured
|
||||
+# update definitions are located in install/share/nis-update.uldif
|
||||
+plugin: update_nis_configuration
|
||||
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
|
||||
index cce2670a6c31803dc534ac5b0093ec0aa317233e..fd96831d8fd4904b51d933847865d37c58a3508c 100644
|
||||
--- a/install/updates/Makefile.am
|
||||
+++ b/install/updates/Makefile.am
|
||||
@@ -52,6 +52,7 @@ app_DATA = \
|
||||
50-groupuuid.update \
|
||||
50-hbacservice.update \
|
||||
50-krbenctypes.update \
|
||||
+ 50-nis.update \
|
||||
50-ipaconfig.update \
|
||||
55-pbacmemberof.update \
|
||||
59-trusts-sysacount.update \
|
||||
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
|
||||
index 5bc0e3ac3eed257fd7fa6a0830e3e2dbeb3d01d2..b84925bfc7d9af8584d9d4b185aa04b635c1e325 100644
|
||||
--- a/ipaplatform/base/paths.py
|
||||
+++ b/ipaplatform/base/paths.py
|
||||
@@ -297,6 +297,8 @@ class BasePathNamespace:
|
||||
CA_TOPOLOGY_ULDIF = "/usr/share/ipa/ca-topology.uldif"
|
||||
IPA_HTML_DIR = "/usr/share/ipa/html"
|
||||
CA_CRT = "/usr/share/ipa/html/ca.crt"
|
||||
+ NIS_ULDIF = "/usr/share/ipa/nis.uldif"
|
||||
+ NIS_UPDATE_ULDIF = "/usr/share/ipa/nis-update.uldif"
|
||||
SCHEMA_COMPAT_ULDIF = "/usr/share/ipa/updates/91-schema_compat.update"
|
||||
SCHEMA_COMPAT_POST_ULDIF = "/usr/share/ipa/schema_compat_post.uldif"
|
||||
SUBID_GENERATORS_ULDIF = "/usr/share/ipa/subid-generators.uldif"
|
||||
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
|
||||
index f07a878e739002346df894f93745ea60345e0557..b0a27de5dd40aa661271faa3a8a0855226b5892f 100644
|
||||
--- a/ipaserver/install/ipa_migrate.py
|
||||
+++ b/ipaserver/install/ipa_migrate.py
|
||||
@@ -32,7 +32,7 @@ from ipapython.admintool import admin_cleanup_global_argv
|
||||
from ipaserver.install.ipa_migrate_constants import (
|
||||
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
|
||||
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
|
||||
- DNA_REGEN_VAL, DNA_REGEN_ATTRS, IGNORE_ATTRS,
|
||||
+ DNA_REGEN_VAL, DNA_REGEN_ATTRS, NIS_PLUGIN, IGNORE_ATTRS,
|
||||
DB_EXCLUDE_TREES, POLICY_OP_ATTRS, STATE_OPTIONS
|
||||
)
|
||||
|
||||
@@ -749,7 +749,8 @@ class IPAMigrate():
|
||||
self.log_info(title)
|
||||
self.log_info('-' * (len(title) - 1))
|
||||
logged_something = self.log_stats(DS_CONFIG)
|
||||
- if self.args.verbose:
|
||||
+ if self.args.verbose or NIS_PLUGIN['count'] > 0:
|
||||
+ self.log_info(f" - NIS Server Plugin: {NIS_PLUGIN['count']}")
|
||||
logged_something = True
|
||||
if not self.log_stats(DS_INDEXES) and not logged_something:
|
||||
self.log_info(" - No updates")
|
||||
@@ -1983,6 +1984,28 @@ class IPAMigrate():
|
||||
add_missing=True)
|
||||
stats['config_processed'] += 1
|
||||
|
||||
+ # Slapi NIS Plugin
|
||||
+ if DN(NIS_PLUGIN['dn']) == DN(entry['dn']):
|
||||
+ # Parent plugin entry
|
||||
+ self.process_config_entry(
|
||||
+ entry['dn'], entry['attrs'], NIS_PLUGIN,
|
||||
+ add_missing=True)
|
||||
+ stats['config_processed'] += 1
|
||||
+ elif DN(NIS_PLUGIN['dn']) in DN(entry['dn']):
|
||||
+ # Child NIS plugin entry
|
||||
+ nis_dn = entry['dn']
|
||||
+ lc_remote_realm = self.remote_realm.lower()
|
||||
+ lc_realm = self.realm.lower()
|
||||
+ nis_dn = nis_dn.replace(lc_remote_realm, lc_realm)
|
||||
+ if 'nis-domain' in entry['attrs']:
|
||||
+ value = entry['attrs']['nis-domain'][0]
|
||||
+ value = value.replace(lc_remote_realm, lc_realm)
|
||||
+ entry['attrs']['nis-domain'][0] = value
|
||||
+ # Process the entry
|
||||
+ self.process_config_entry(nis_dn, entry['attrs'], NIS_PLUGIN,
|
||||
+ add_missing=True)
|
||||
+ stats['config_processed'] += 1
|
||||
+
|
||||
#
|
||||
# Migration
|
||||
#
|
||||
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
|
||||
index 65e920d4bdc7511adb60bbdf87db88beb2630645..4fe8b467978936e4f8d621b7297ff577127c7298 100644
|
||||
--- a/ipaserver/install/ipa_migrate_constants.py
|
||||
+++ b/ipaserver/install/ipa_migrate_constants.py
|
||||
@@ -527,6 +527,30 @@ DS_CONFIG = {
|
||||
},
|
||||
}
|
||||
|
||||
+#
|
||||
+# Slpai NIS is an optional plugin. It requires special handling
|
||||
+#
|
||||
+NIS_PLUGIN = {
|
||||
+ 'dn': 'cn=NIS Server,cn=plugins,cn=config',
|
||||
+ 'attrs': [
|
||||
+ 'nis-domain',
|
||||
+ 'nis-base',
|
||||
+ 'nis-map',
|
||||
+ 'nis-filter',
|
||||
+ 'nis-key-format:',
|
||||
+ 'nis-values-format:',
|
||||
+ 'nis-secure',
|
||||
+ 'nis-disallowed-chars',
|
||||
+ # Parent plugin entry
|
||||
+ 'nsslapd-pluginarg0',
|
||||
+ 'nsslapd-pluginenabled'
|
||||
+ ],
|
||||
+ 'multivalued': [],
|
||||
+ 'label': 'NIS Server Plugin',
|
||||
+ 'mode': 'all',
|
||||
+ 'count': 0,
|
||||
+}
|
||||
+
|
||||
#
|
||||
# This mapping is simliar to above but it handles container entries
|
||||
# This could be built into the above mapping using the "comma" approach
|
||||
diff --git a/ipaserver/install/plugins/update_nis.py b/ipaserver/install/plugins/update_nis.py
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c02eb5f838a99b27cdd21e4aee17e5104f0e22e2
|
||||
--- /dev/null
|
||||
+++ b/ipaserver/install/plugins/update_nis.py
|
||||
@@ -0,0 +1,92 @@
|
||||
+#
|
||||
+# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
+#
|
||||
+
|
||||
+from __future__ import absolute_import
|
||||
+
|
||||
+import logging
|
||||
+
|
||||
+from ipalib.plugable import Registry
|
||||
+from ipalib import errors
|
||||
+from ipalib import Updater
|
||||
+from ipaplatform.paths import paths
|
||||
+from ipapython.dn import DN
|
||||
+from ipaserver.install import sysupgrade
|
||||
+from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
+
|
||||
+logger = logging.getLogger(__name__)
|
||||
+
|
||||
+register = Registry()
|
||||
+
|
||||
+
|
||||
+@register()
|
||||
+class update_nis_configuration(Updater):
|
||||
+ """Update NIS configuration
|
||||
+
|
||||
+ NIS configuration can be updated only if NIS Server was configured via
|
||||
+ ipa-nis-manage command.
|
||||
+ """
|
||||
+
|
||||
+ def __recover_from_missing_maps(self, ldap):
|
||||
+ # https://fedorahosted.org/freeipa/ticket/5507
|
||||
+ # if all following DNs are missing, but 'NIS Server' container exists
|
||||
+ # we are experiencig bug and maps should be fixed
|
||||
+
|
||||
+ if sysupgrade.get_upgrade_state('nis',
|
||||
+ 'done_recover_from_missing_maps'):
|
||||
+ # this recover must be done only once, a user may deleted some
|
||||
+ # maps, we do not want to restore them again
|
||||
+ return
|
||||
+
|
||||
+ logger.debug("Recovering from missing NIS maps bug")
|
||||
+
|
||||
+ suffix = "cn=NIS Server,cn=plugins,cn=config"
|
||||
+ domain = self.api.env.domain
|
||||
+ missing_dn_list = [
|
||||
+ DN(nis_map.format(domain=domain, suffix=suffix)) for nis_map in [
|
||||
+ "nis-domain={domain}+nis-map=passwd.byname,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=passwd.byuid,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=group.byname,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=group.bygid,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=netid.byname,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=netgroup,{suffix}",
|
||||
+ ]
|
||||
+ ]
|
||||
+
|
||||
+ for dn in missing_dn_list:
|
||||
+ try:
|
||||
+ ldap.get_entry(dn, attrs_list=['cn'])
|
||||
+ except errors.NotFound:
|
||||
+ pass
|
||||
+ else:
|
||||
+ # bug is not effective, at least one of 'possible missing'
|
||||
+ # maps was detected
|
||||
+ return
|
||||
+
|
||||
+ sysupgrade.set_upgrade_state('nis', 'done_recover_from_missing_maps',
|
||||
+ True)
|
||||
+
|
||||
+ # bug is effective run update to recreate missing maps
|
||||
+ ld = LDAPUpdate(api=self.api)
|
||||
+ ld.update([paths.NIS_ULDIF])
|
||||
+
|
||||
+ def execute(self, **options):
|
||||
+ ldap = self.api.Backend.ldap2
|
||||
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+ try:
|
||||
+ ldap.get_entry(dn, attrs_list=['cn'])
|
||||
+ except errors.NotFound:
|
||||
+ # NIS is not configured on system, do not execute update
|
||||
+ logger.debug("Skipping NIS update, NIS Server is not configured")
|
||||
+
|
||||
+ # container does not exist, bug #5507 is not effective
|
||||
+ sysupgrade.set_upgrade_state(
|
||||
+ 'nis', 'done_recover_from_missing_maps', True)
|
||||
+ else:
|
||||
+ self.__recover_from_missing_maps(ldap)
|
||||
+
|
||||
+ logger.debug("Executing NIS Server update")
|
||||
+ ld = LDAPUpdate(api=self.api)
|
||||
+ ld.update([paths.NIS_UPDATE_ULDIF])
|
||||
+
|
||||
+ return False, ()
|
||||
diff --git a/ipatests/test_cmdline/test_cli.py b/ipatests/test_cmdline/test_cli.py
|
||||
index 6c86bbb657a0d9a7b74ef34ad20a796a10073315..4acf8dd5290c687723a9c43a21e194bf663b6a3b 100644
|
||||
--- a/ipatests/test_cmdline/test_cli.py
|
||||
+++ b/ipatests/test_cmdline/test_cli.py
|
||||
@@ -372,6 +372,7 @@ IPA_CLIENT_NOT_CONFIGURED = b'IPA client is not configured on this system'
|
||||
'/usr/share/ipa/updates/05-pre_upgrade_plugins.update'],
|
||||
2, None, IPA_NOT_CONFIGURED),
|
||||
(['ipa-managed-entries'], 2, None, IPA_NOT_CONFIGURED),
|
||||
+ (['ipa-nis-manage'], 2, None, IPA_NOT_CONFIGURED),
|
||||
(['ipa-pkinit-manage'], 2, None, IPA_NOT_CONFIGURED),
|
||||
(['ipa-replica-manage', 'list'], 1, IPA_NOT_CONFIGURED, None),
|
||||
(['ipa-server-certinstall'], 2, None, IPA_NOT_CONFIGURED),
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 5bc871ab71bc05ec1c26df8352e996a9e627b466..8f8548494eaec3afd00961c096772b9d0b1ab3a4 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -1382,6 +1382,93 @@ class TestIPACommand(IntegrationTest):
|
||||
serverid = realm_to_serverid(self.master.domain.realm)
|
||||
return ("dirsrv@%s.service" % serverid)
|
||||
|
||||
+ def test_ipa_nis_manage_enable(self):
|
||||
+ """
|
||||
+ This testcase checks if ipa-nis-manage enable
|
||||
+ command enables plugin on an IPA master
|
||||
+ """
|
||||
+ dirsrv_service = self.get_dirsrv_id()
|
||||
+ console_msg = (
|
||||
+ "Enabling plugin\n"
|
||||
+ "This setting will not take effect until "
|
||||
+ "you restart Directory Server.\n"
|
||||
+ "The rpcbind service may need to be started"
|
||||
+ )
|
||||
+ status_msg = "Plugin is enabled"
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "enable"],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ )
|
||||
+ assert console_msg in result.stdout_text
|
||||
+ # verify using backend
|
||||
+ conn = self.master.ldap_connect()
|
||||
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+ entry = conn.get_entry(dn)
|
||||
+ nispluginstring = entry.get('nsslapd-pluginEnabled')
|
||||
+ assert 'on' in nispluginstring
|
||||
+ # restart for changes to take effect
|
||||
+ self.master.run_command(["systemctl", "restart", dirsrv_service])
|
||||
+ self.master.run_command(["systemctl", "restart", "rpcbind"])
|
||||
+ time.sleep(DIRSRV_SLEEP)
|
||||
+ # check status msg on the console
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "status"],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ )
|
||||
+ assert status_msg in result.stdout_text
|
||||
+
|
||||
+ def test_ipa_nis_manage_disable(self):
|
||||
+ """
|
||||
+ This testcase checks if ipa-nis-manage disable
|
||||
+ command disable plugin on an IPA Master
|
||||
+ """
|
||||
+ dirsrv_service = self.get_dirsrv_id()
|
||||
+ msg = (
|
||||
+ "This setting will not take effect "
|
||||
+ "until you restart Directory Server."
|
||||
+ )
|
||||
+ status_msg = "Plugin is not enabled"
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "disable"],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ )
|
||||
+ assert msg in result.stdout_text
|
||||
+ # verify using backend
|
||||
+ conn = self.master.ldap_connect()
|
||||
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+ entry = conn.get_entry(dn)
|
||||
+ nispluginstring = entry.get('nsslapd-pluginEnabled')
|
||||
+ assert 'off' in nispluginstring
|
||||
+ # restart dirsrv for changes to take effect
|
||||
+ self.master.run_command(["systemctl", "restart", dirsrv_service])
|
||||
+ time.sleep(DIRSRV_SLEEP)
|
||||
+ # check status msg on the console
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "status"],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+ assert result.returncode == 4
|
||||
+ assert status_msg in result.stdout_text
|
||||
+
|
||||
+ def test_ipa_nis_manage_enable_incorrect_password(self):
|
||||
+ """
|
||||
+ This testcase checks if ipa-nis-manage enable
|
||||
+ command throws error on console for invalid DS admin password
|
||||
+ """
|
||||
+ msg1 = "Insufficient access: "
|
||||
+ msg2 = "Invalid credentials"
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "enable"],
|
||||
+ stdin_text='Invalid_pwd',
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+ assert result.returncode == 1
|
||||
+ assert msg1 in result.stderr_text
|
||||
+ assert msg2 in result.stderr_text
|
||||
+
|
||||
def test_pkispawn_log_is_present(self):
|
||||
"""
|
||||
This testcase checks if pkispawn logged properly.
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,92 +0,0 @@
|
||||
From ad4b7f6cedaed54acf279033b650010c65face10 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Tue, 20 Aug 2024 14:52:03 +0530
|
||||
Subject: [PATCH] ipatests: Check Default PAC type is added to config
|
||||
|
||||
This patch checks that the default PAC type
|
||||
is added to configuration i.e ipaKrbAuthzData: MS-PAC
|
||||
during ipa-server-installation
|
||||
|
||||
The patch also checks that if 'ipaKrbAuthzData: MS-PAC'
|
||||
attribute is deleted and then when we run 'ipa-server-upgrade'
|
||||
command the attribute is added back.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9632
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
.../test_integration/test_installation.py | 15 +++++++++++
|
||||
ipatests/test_integration/test_upgrade.py | 26 ++++++++++++++++++-
|
||||
2 files changed, 40 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py
|
||||
index ada43e33fe173ea3c315178c37e2a664b05b905b..c5565c452010f23f038ddf329454b591ef09f6af 100644
|
||||
--- a/ipatests/test_integration/test_installation.py
|
||||
+++ b/ipatests/test_integration/test_installation.py
|
||||
@@ -1190,6 +1190,21 @@ class TestInstallMaster(IntegrationTest):
|
||||
expected_stdout=f'href="https://{self.master.hostname}/'
|
||||
)
|
||||
|
||||
+ def test_pac_configuration_enabled(self):
|
||||
+ """
|
||||
+ This testcase checks that the default PAC type
|
||||
+ is added to configuration.
|
||||
+ """
|
||||
+ base_dn = str(self.master.domain.basedn)
|
||||
+ dn = DN(
|
||||
+ ("cn", "ipaConfig"),
|
||||
+ ("cn", "etc"),
|
||||
+ base_dn
|
||||
+ )
|
||||
+ result = tasks.ldapsearch_dm(self.master, str(dn),
|
||||
+ ["ipaKrbAuthzData"])
|
||||
+ assert 'ipaKrbAuthzData: MS-PAC' in result.stdout_text
|
||||
+
|
||||
def test_hostname_parameter(self, server_cleanup):
|
||||
"""
|
||||
Test that --hostname parameter is respected in interactive mode.
|
||||
diff --git a/ipatests/test_integration/test_upgrade.py b/ipatests/test_integration/test_upgrade.py
|
||||
index 011de939e92790734d63da2f85be1c25349116a8..a0f393780ccc25774466992976532c876aa876da 100644
|
||||
--- a/ipatests/test_integration/test_upgrade.py
|
||||
+++ b/ipatests/test_integration/test_upgrade.py
|
||||
@@ -165,7 +165,6 @@ class TestUpgrade(IntegrationTest):
|
||||
ldap.update_entry(location_krb_rec)
|
||||
|
||||
yield _setup_locations
|
||||
-
|
||||
ldap = self.master.ldap_connect()
|
||||
|
||||
modified = False
|
||||
@@ -491,3 +490,28 @@ class TestUpgrade(IntegrationTest):
|
||||
tasks.reinstall_packages(self.master, ['*ipa-client'])
|
||||
assert not self.master.transport.file_exists(
|
||||
paths.SSH_CONFIG + ".orig")
|
||||
+
|
||||
+ def test_mspac_attribute_set(self):
|
||||
+ """
|
||||
+ This testcase deletes the already existing attribute
|
||||
+ 'ipaKrbAuthzData: MS-PAC'.
|
||||
+ The test then runs ipa-server-upgrade and checks that
|
||||
+ the attribute 'ipaKrbAuthzData: MS-PAC' is added again.
|
||||
+ """
|
||||
+ base_dn = str(self.master.domain.basedn)
|
||||
+ dn = DN(
|
||||
+ ("cn", "ipaConfig"),
|
||||
+ ("cn", "etc"),
|
||||
+ base_dn
|
||||
+ )
|
||||
+ ldif = textwrap.dedent("""
|
||||
+ dn: cn=ipaConfig,cn=etc,{}
|
||||
+ changetype: modify
|
||||
+ delete: ipaKrbAuthzData
|
||||
+ """).format(base_dn)
|
||||
+ tasks.ldapmodify_dm(self.master, ldif)
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ self.master.run_command(['ipa-server-upgrade'])
|
||||
+ result = tasks.ldapsearch_dm(self.master, str(dn),
|
||||
+ ["ipaKrbAuthzData"])
|
||||
+ assert 'ipaKrbAuthzData: MS-PAC' in result.stdout_text
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
From 42eb97ee6bd8011b590aef321d4386ea9352933d Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Wed, 28 Aug 2024 10:02:19 +0300
|
||||
Subject: [PATCH] selinux: add all IPA log files to ipa_log_t file context
|
||||
|
||||
We have multiple log files that produced by IPA components. Some of them
|
||||
are written by the tools that run as root and inherit their file context
|
||||
from /var/log -> var_log_t. However, increasingly we get tools that were
|
||||
run through oddjob helpers. These supposed to be run within ipa_helper_t
|
||||
SELinux context which has write permissions for ipa_log_t file context.
|
||||
|
||||
Add all known log files from the base platform. The following script was
|
||||
used to generate them:
|
||||
$ git grep '_LOG = .*ipa.*\.log' ipaplatform/base/paths.py | cut -d= -f2 | \
|
||||
xargs -I% echo -e "%\t--\tgen_context(system_u:object_r:ipa_log_t,s0)"
|
||||
|
||||
/var/log/ipabackup.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaclient-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaclient-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaclientsamba-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaclientsamba-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipareplica-ca-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipareplica-conncheck.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipareplica-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/iparestore.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaserver-enable-sid.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaserver-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaserver-adtrust-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaserver-dns-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaserver-kra-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaserver-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaupgrade.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipatrust-enable-agent.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipaepn.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipa-custodia.audit.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
/var/log/ipa-migrate.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
|
||||
ipa-custodia.audit.log was already in the present list.
|
||||
|
||||
Additionally, ipa-migrate-conflict.ldif is used by the ipa-migrate tool
|
||||
but is not provided through the ipaplatform mechanism. It is added
|
||||
explicitly.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9654
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
selinux/ipa.fc | 21 ++++++++++++++++++++-
|
||||
1 file changed, 20 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/selinux/ipa.fc b/selinux/ipa.fc
|
||||
index 700e3a14a11fcd403a2e6f57ec781c58dae77660..47bd19ba77418cad1f0904dc4a9a35ce9d6ff9d2 100644
|
||||
--- a/selinux/ipa.fc
|
||||
+++ b/selinux/ipa.fc
|
||||
@@ -24,7 +24,26 @@
|
||||
|
||||
/var/log/ipa(/.*)? gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
|
||||
-/var/log/ipareplica-conncheck.log.* -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipabackup.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaclient-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaclient-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaclientsamba-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaclientsamba-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipareplica-ca-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipareplica-conncheck.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipareplica-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/iparestore.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaserver-enable-sid.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaserver-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaserver-adtrust-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaserver-dns-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaserver-kra-install.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaserver-uninstall.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaupgrade.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipatrust-enable-agent.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipaepn.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipa-migrate.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
+/var/log/ipa-migrate-conflict.ldif -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
|
||||
/var/run/ipa(/.*)? gen_context(system_u:object_r:ipa_var_run_t,s0)
|
||||
|
||||
--
|
||||
2.46.2
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
321
SOURCES/0006-Revert-Stop-using-deprecated-pkg_resources.patch
Normal file
321
SOURCES/0006-Revert-Stop-using-deprecated-pkg_resources.patch
Normal file
@ -0,0 +1,321 @@
|
||||
From 257740b66daf004a7333bfaeddece4732be5a48c Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 9 Dec 2025 17:52:36 +0100
|
||||
Subject: [PATCH] Revert "Stop using deprecated pkg_resources"
|
||||
|
||||
This reverts commit ac791f7372d32d25c75eb61f949f1db38fe2f0d6.
|
||||
---
|
||||
freeipa.spec.in | 6 +++++-
|
||||
ipaplatform/base/tasks.py | 2 +-
|
||||
ipapython/version.py.in | 2 +-
|
||||
ipaserver/custodia/server/__init__.py | 13 +++++++++----
|
||||
ipaserver/install/cainstance.py | 2 +-
|
||||
ipaserver/install/krbinstance.py | 2 +-
|
||||
ipaserver/install/server/replicainstall.py | 2 +-
|
||||
ipaserver/install/server/upgrade.py | 2 +-
|
||||
.../pytest_ipa/integration/create_caless_pki.py | 2 +-
|
||||
ipatests/pytest_ipa/integration/tasks.py | 2 +-
|
||||
ipatests/test_custodia/test_plugins.py | 13 ++++++++-----
|
||||
ipatests/test_integration/test_adtrust_install.py | 2 +-
|
||||
ipatests/test_integration/test_cert.py | 2 +-
|
||||
ipatests/test_integration/test_commands.py | 2 +-
|
||||
ipatests/test_integration/test_idp.py | 2 +-
|
||||
ipatests/test_integration/test_ipahealthcheck.py | 2 +-
|
||||
ipatests/test_webui/ui_driver.py | 2 +-
|
||||
ipatests/test_xmlrpc/test_automember_plugin.py | 1 +
|
||||
18 files changed, 37 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||
index b93199a5783a89e0ff58affd3d416556f72dd00c..bf6fa216dcc4cfbac903aad43c6219f1e57db4dd 100644
|
||||
--- a/freeipa.spec.in
|
||||
+++ b/freeipa.spec.in
|
||||
@@ -983,7 +983,6 @@ Requires: python3-jwcrypto >= 0.4.2
|
||||
Requires: python3-libipa_hbac
|
||||
Requires: python3-netaddr >= %{python_netaddr_version}
|
||||
Requires: python3-netifaces >= 0.10.4
|
||||
-Requires: python3-packaging
|
||||
Requires: python3-pyasn1 >= 0.3.2-2
|
||||
Requires: python3-pyasn1-modules >= 0.3.2-2
|
||||
Requires: python3-pyusb
|
||||
@@ -992,6 +991,11 @@ Requires: python3-requests
|
||||
Requires: python3-six
|
||||
Requires: python3-sss-murmur
|
||||
Requires: python3-yubico >= 1.3.2-7
|
||||
+%if 0%{?rhel} && 0%{?rhel} == 8
|
||||
+Requires: platform-python-setuptools
|
||||
+%else
|
||||
+Requires: python3-setuptools
|
||||
+%endif
|
||||
%if 0%{?rhel}
|
||||
Requires: python3-urllib3 >= 1.24.2-3
|
||||
%else
|
||||
diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py
|
||||
index 9e221d872e7ca9ac0607ff29e1b51dedcf688d75..ab3563aba3decf370a72c9ec273c8bccc2d85ca2 100644
|
||||
--- a/ipaplatform/base/tasks.py
|
||||
+++ b/ipaplatform/base/tasks.py
|
||||
@@ -29,7 +29,7 @@ import os
|
||||
import logging
|
||||
import textwrap
|
||||
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython import ipautil
|
||||
diff --git a/ipapython/version.py.in b/ipapython/version.py.in
|
||||
index eee8900be5fa72759fd7ee87f72952599231e138..a8f4218a7bac2a2f52389ad3dc5f31a98a822e82 100644
|
||||
--- a/ipapython/version.py.in
|
||||
+++ b/ipapython/version.py.in
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
# The full version including strings
|
||||
VERSION = "@VERSION@"
|
||||
diff --git a/ipaserver/custodia/server/__init__.py b/ipaserver/custodia/server/__init__.py
|
||||
index 26ca0a481559da101891034798e0434f063367b8..e713de20d0e9ff42e65ed3d2170d6446baa8d4e8 100644
|
||||
--- a/ipaserver/custodia/server/__init__.py
|
||||
+++ b/ipaserver/custodia/server/__init__.py
|
||||
@@ -2,9 +2,10 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import importlib
|
||||
-import importlib.metadata
|
||||
import os
|
||||
|
||||
+import pkg_resources
|
||||
+
|
||||
import six
|
||||
|
||||
from ipaserver.custodia import log
|
||||
@@ -36,13 +37,17 @@ def _load_plugin_class(menu, name):
|
||||
Entry points are preferred over dotted import path.
|
||||
"""
|
||||
group = 'custodia.{}'.format(menu)
|
||||
- eps = importlib.metadata.entry_points(group=group, name=name)
|
||||
+ eps = list(pkg_resources.iter_entry_points(group, name))
|
||||
if len(eps) > 1:
|
||||
raise ValueError(
|
||||
"Multiple entry points for {} {}: {}".format(menu, name, eps))
|
||||
elif len(eps) == 1:
|
||||
- ep, *_ = eps
|
||||
- return ep.load(require=False)
|
||||
+ # backwards compatibility with old setuptools
|
||||
+ ep = eps[0]
|
||||
+ if hasattr(ep, 'resolve'):
|
||||
+ return ep.resolve()
|
||||
+ else:
|
||||
+ return ep.load(require=False)
|
||||
elif '.' in name:
|
||||
# fall back to old style dotted name
|
||||
module, classname = name.rsplit('.', 1)
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index b8267a625554f9375d27160f39b67ee2e64a2dbb..a92b3ed6f01db97a86464e5cb77a084f8a1de90f 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -35,7 +35,7 @@ import syslog
|
||||
import time
|
||||
import tempfile
|
||||
from configparser import RawConfigParser
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipalib import api
|
||||
from ipalib import x509
|
||||
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
|
||||
index 4a521a611bd1bebf925061ea88a60876dff48467..0f568122c94aab8e55604179003276d29580de76 100644
|
||||
--- a/ipaserver/install/krbinstance.py
|
||||
+++ b/ipaserver/install/krbinstance.py
|
||||
@@ -27,7 +27,7 @@ import tempfile
|
||||
import dbus
|
||||
|
||||
import dns.name
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipalib import x509
|
||||
from ipalib.install import certstore
|
||||
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
|
||||
index b4d06d8e24097e12c97a8b881dfcc9b028467272..5b4f0161083bbf65ac423ebacad30c5743ffab8e 100644
|
||||
--- a/ipaserver/install/server/replicainstall.py
|
||||
+++ b/ipaserver/install/server/replicainstall.py
|
||||
@@ -18,7 +18,7 @@ import tempfile
|
||||
import textwrap
|
||||
import traceback
|
||||
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
import six
|
||||
|
||||
from ipaclient.install.client import check_ldap_conf, sssd_enable_ifp
|
||||
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
|
||||
index 548ee02e1e8524ce0002dca1764d48728eb0509a..d9cb6656b4731b5cb803b870469635862f4269e5 100644
|
||||
--- a/ipaserver/install/server/upgrade.py
|
||||
+++ b/ipaserver/install/server/upgrade.py
|
||||
@@ -17,7 +17,7 @@ import sys
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from augeas import Augeas
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipalib import api, x509
|
||||
from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE, IPA_CA_RECORD
|
||||
diff --git a/ipatests/pytest_ipa/integration/create_caless_pki.py b/ipatests/pytest_ipa/integration/create_caless_pki.py
|
||||
index d06f1dd8c328628bd692c2abf3acfc88ba6a7408..01d64462ce76eb76cf7efdbeb850955ca4990535 100644
|
||||
--- a/ipatests/pytest_ipa/integration/create_caless_pki.py
|
||||
+++ b/ipatests/pytest_ipa/integration/create_caless_pki.py
|
||||
@@ -26,7 +26,7 @@ from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.x509.oid import NameOID
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
from pyasn1.type import univ, char, namedtype, tag
|
||||
from pyasn1.codec.der import encoder as der_encoder
|
||||
from pyasn1.codec.native import decoder as native_decoder
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index 3ef361807ee0e1c858f4bef8b7d9e26c5f0fa20a..c6e0cf3937a12c7bb13da4b83250ecced470b543 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -36,7 +36,7 @@ import time
|
||||
from shlex import quote
|
||||
import configparser
|
||||
from contextlib import contextmanager
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
import uuid
|
||||
|
||||
import dns
|
||||
diff --git a/ipatests/test_custodia/test_plugins.py b/ipatests/test_custodia/test_plugins.py
|
||||
index 9d3f6c24ff2eecad9e9a1c574aaae2edd30e4921..be8aa936db2bc553e0a13bb0975e88d186e3da1c 100644
|
||||
--- a/ipatests/test_custodia/test_plugins.py
|
||||
+++ b/ipatests/test_custodia/test_plugins.py
|
||||
@@ -1,6 +1,5 @@
|
||||
# Copyright (C) 2016 Custodia Project Contributors - see LICENSE file
|
||||
-import importlib.metadata
|
||||
-
|
||||
+import pkg_resources
|
||||
import pytest
|
||||
|
||||
from ipaserver.custodia.plugin import (
|
||||
@@ -13,8 +12,8 @@ class TestCustodiaPlugins:
|
||||
|
||||
def get_entry_points(self, group):
|
||||
eps = []
|
||||
- for e in importlib.metadata.entry_points(group=group):
|
||||
- if e.dist.name != self.project_name:
|
||||
+ for e in pkg_resources.iter_entry_points(group):
|
||||
+ if e.dist.project_name != self.project_name:
|
||||
# only interested in our own entry points
|
||||
continue
|
||||
eps.append(e)
|
||||
@@ -22,7 +21,11 @@ class TestCustodiaPlugins:
|
||||
|
||||
def assert_ep(self, ep, basecls):
|
||||
try:
|
||||
- cls = ep.load(require=False)
|
||||
+ # backwards compatibility with old setuptools
|
||||
+ if hasattr(ep, "resolve"):
|
||||
+ cls = ep.resolve()
|
||||
+ else:
|
||||
+ cls = ep.load(require=False)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
pytest.fail("Failed to load %r: %r" % (ep, e))
|
||||
if not issubclass(cls, basecls):
|
||||
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
|
||||
index 09e227ec8125e90b37d1d92f0512f9819f5b48c3..ac7e5663aa1c70184f5a09ea5e14cca8c671d78e 100644
|
||||
--- a/ipatests/test_integration/test_adtrust_install.py
|
||||
+++ b/ipatests/test_integration/test_adtrust_install.py
|
||||
@@ -13,7 +13,7 @@ from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
import pytest
|
||||
|
||||
diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py
|
||||
index 21568c2421c21855df06bcf5fbb4d52b3651a523..03ea5cfb000200d4d77b97d8808e30dbd5392871 100644
|
||||
--- a/ipatests/test_integration/test_cert.py
|
||||
+++ b/ipatests/test_integration/test_cert.py
|
||||
@@ -20,7 +20,7 @@ from ipapython.dn import DN
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import ExtensionOID
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 5bc871ab71bc05ec1c26df8352e996a9e627b466..04d8f71ff922f98c71331fa9a5f0f6508dd3fc12 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -41,7 +41,7 @@ from ipatests.test_ipalib.test_x509 import good_pkcs7, badcert
|
||||
from ipapython.ipautil import realm_to_suffix, ipa_generate_password
|
||||
from ipatests.test_integration.test_topology import find_segment
|
||||
from ipaserver.install.installutils import realm_to_serverid
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_idp.py b/ipatests/test_integration/test_idp.py
|
||||
index 9983b3c9d62826afa395c2739b4fb9945afed80d..a29333ef232b0ae8229484e404866d8bb720554a 100644
|
||||
--- a/ipatests/test_integration/test_idp.py
|
||||
+++ b/ipatests/test_integration/test_idp.py
|
||||
@@ -12,7 +12,7 @@ from ipatests.pytest_ipa.integration import tasks, create_keycloak
|
||||
user_code_script = textwrap.dedent("""
|
||||
from selenium import webdriver
|
||||
from datetime import datetime
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
index 47392cec9345b6d2447b728fc21050edabe8f0cd..15fc38a6e155b343259ee61c6c7c5c561c7f6410 100644
|
||||
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
||||
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
@@ -28,7 +28,7 @@ from ipaplatform.osinfo import osinfo
|
||||
from ipaserver.install.installutils import resolve_ip_addresses_nss
|
||||
from ipatests.test_integration.test_caless import CALessBase
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
from ipatests.test_integration.test_cert import get_certmonger_fs_id
|
||||
from ipatests.test_integration.test_external_ca import (
|
||||
install_server_external_ca_step1,
|
||||
diff --git a/ipatests/test_webui/ui_driver.py b/ipatests/test_webui/ui_driver.py
|
||||
index 356c4a0998cf01a6dbf1f207f38db87504aa5522..5dcea8979346119b2c11af7cf3f0b8c9b438cb40 100644
|
||||
--- a/ipatests/test_webui/ui_driver.py
|
||||
+++ b/ipatests/test_webui/ui_driver.py
|
||||
@@ -29,7 +29,7 @@ import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
from urllib.error import URLError
|
||||
|
||||
import pytest
|
||||
diff --git a/ipatests/test_xmlrpc/test_automember_plugin.py b/ipatests/test_xmlrpc/test_automember_plugin.py
|
||||
index aa7c1d65a7059a6a1e911f31204c05a42b6d9c3f..94f1b10985de61046bb65c092ab3d31c6f99e17c 100644
|
||||
--- a/ipatests/test_xmlrpc/test_automember_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_automember_plugin.py
|
||||
@@ -35,6 +35,7 @@ from ipaserver.plugins.automember import REBUILD_TASK_CONTAINER
|
||||
|
||||
import time
|
||||
import pytest
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
try:
|
||||
from ipaserver.plugins.ldap2 import ldap2
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
From 4fef80aeaaf017b286bd12ebfc30529f6a65a80e Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Mon, 2 Sep 2024 18:28:27 +0200
|
||||
Subject: [PATCH] ipatests: Add missing comma in
|
||||
test_idrange_no_rid_bases_reversed
|
||||
|
||||
The test is calling ipa idrange-add but is missing a comma in
|
||||
the arguments list.
|
||||
The resulting call is using "--rid-base 100300000--secondary-rid-base".
|
||||
Add the missing comma to build the command with
|
||||
"--rid-base 100300000 --secondary-rid-base"
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9656
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_ipa_idrange_fix.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipa_idrange_fix.py b/ipatests/test_integration/test_ipa_idrange_fix.py
|
||||
index de3da9bfd221ce74f1d1bbb0dbe12e4db08b8daa..ff8fbdac9d028d26fc55f5e357f89af879a61723 100644
|
||||
--- a/ipatests/test_integration/test_ipa_idrange_fix.py
|
||||
+++ b/ipatests/test_integration/test_ipa_idrange_fix.py
|
||||
@@ -72,7 +72,7 @@ class TestIpaIdrangeFix(IntegrationTest):
|
||||
"idrange_reversed",
|
||||
"--base-id", '50000',
|
||||
"--range-size", '20000',
|
||||
- "--rid-base", '100300000'
|
||||
+ "--rid-base", '100300000',
|
||||
"--secondary-rid-base", '301000'
|
||||
])
|
||||
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
From a18eb8358675b3697ccf8f8d8dc230cc62df6a4d Mon Sep 17 00:00:00 2001
|
||||
From: Erik Belko <ebelko@redhat.com>
|
||||
Date: Thu, 29 Aug 2024 16:47:21 +0200
|
||||
Subject: [PATCH] ipatests: Update ipa-adtrust-install test
|
||||
|
||||
update test_user_connects_smb_share_if_locked_specific_group with wait
|
||||
for SSSD to be online after ipa-adtrust-install command
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9655
|
||||
|
||||
Signed-off-by: Erik Belko <ebelko@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_adtrust_install.py | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
|
||||
index 72e8d874fb17adadc556ba55b825a88a3ac21a67..de252db1705ad940c3b5ee4df967d7c17a4203a7 100644
|
||||
--- a/ipatests/test_integration/test_adtrust_install.py
|
||||
+++ b/ipatests/test_integration/test_adtrust_install.py
|
||||
@@ -853,6 +853,8 @@ class TestIpaAdTrustInstall(IntegrationTest):
|
||||
self.master.config.admin_password,
|
||||
"-U"]
|
||||
)
|
||||
+ # Wait for SSSD to become online before doing any other check
|
||||
+ tasks.wait_for_sssd_domain_status_online(self.master)
|
||||
self.master.run_command(["mkdir", "/freeipa4234"])
|
||||
self.master.run_command(
|
||||
["chcon", "-t", "samba_share_t",
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
From 373d41f211c1a04dc432a068bc7d2ba825ff554c Mon Sep 17 00:00:00 2001
|
||||
From: Francisco Trivino <ftrivino@redhat.com>
|
||||
Date: Tue, 13 Aug 2024 12:44:21 +0200
|
||||
Subject: [PATCH] Installer: activate ssh service in sssd.conf
|
||||
|
||||
This commit enables SSSD's ssh service in ipa-client-install to ensure
|
||||
sss_ssh_knownhosts and sss_ssh_knownhostsproxy functions properly.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9649
|
||||
Related: https://pagure.io/freeipa/issue/9536
|
||||
|
||||
Signed-off-by: Francisco Trivino <ftrivino@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaclient/install/client.py | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
|
||||
index 802db9614b24553b2b49259f3aebb366093560ac..47a371f629f6ddfb1cd5e9fff9faad737aa01f54 100644
|
||||
--- a/ipaclient/install/client.py
|
||||
+++ b/ipaclient/install/client.py
|
||||
@@ -974,6 +974,8 @@ def configure_sssd_conf(
|
||||
|
||||
sssd_enable_service(sssdconfig, 'nss')
|
||||
sssd_enable_service(sssdconfig, 'pam')
|
||||
+ if options.conf_ssh:
|
||||
+ sssd_enable_service(sssdconfig, 'ssh')
|
||||
|
||||
domain.set_option('ipa_domain', cli_domain)
|
||||
domain.set_option('ipa_hostname', client_hostname)
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,413 +0,0 @@
|
||||
From 8d242ba741ec22b258d5e70a530cefd0940783c7 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Tue, 23 Jul 2024 17:07:06 -0400
|
||||
Subject: [PATCH] ipa-migrate - fix migration issues with entries using
|
||||
ipaUniqueId in the RDN
|
||||
|
||||
We need to handle these entries differently and specify what attribute
|
||||
and search base to use to find the entry on the local server. Most
|
||||
entries can use the "cn" attribute but for selinux usermaps we need to
|
||||
search using the ipaOwner attribute which is a DN, and in turn requires
|
||||
additional handling/converting in order to properly check if the usermap
|
||||
exists or not.
|
||||
|
||||
Also fixed an issue where an attribute should be removed from the local
|
||||
entry if it does not exist on the remote entry.
|
||||
|
||||
And fixed the handling od "sudoOrder" which is defined as multi-valued
|
||||
in the schema, but we really need to treat it as single-valued
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9640
|
||||
|
||||
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/ipa_migrate.py | 119 +++++++++++++++++++--
|
||||
ipaserver/install/ipa_migrate_constants.py | 84 +++++++++++++--
|
||||
2 files changed, 188 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
|
||||
index e21937401b3463335d8297b41a403405071d3795..78c530f24fe5d8c9f5de0f816df9904bf30c7b94 100644
|
||||
--- a/ipaserver/install/ipa_migrate.py
|
||||
+++ b/ipaserver/install/ipa_migrate.py
|
||||
@@ -32,7 +32,7 @@ from ipaserver.install.ipa_migrate_constants import (
|
||||
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
|
||||
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
|
||||
DNA_REGEN_VAL, DNA_REGEN_ATTRS, NIS_PLUGIN, IGNORE_ATTRS,
|
||||
- DB_EXCLUDE_TREES
|
||||
+ DB_EXCLUDE_TREES, POLICY_OP_ATTRS
|
||||
)
|
||||
|
||||
"""
|
||||
@@ -529,6 +529,14 @@ class IPAMigrate():
|
||||
#
|
||||
# Helper functions
|
||||
#
|
||||
+ def attr_is_operational(self, attr):
|
||||
+ schema = self.local_conn.schema
|
||||
+ attr_obj = schema.get_obj(ldap.schema.AttributeType, attr)
|
||||
+ if attr_obj is not None:
|
||||
+ if attr_obj.usage == 1:
|
||||
+ return True
|
||||
+ return False
|
||||
+
|
||||
def replace_suffix(self, entry_dn):
|
||||
"""
|
||||
Replace the base DN in an entry DN
|
||||
@@ -1122,6 +1130,18 @@ class IPAMigrate():
|
||||
stats['reset_range'] += 1
|
||||
return entry
|
||||
|
||||
+ def attr_is_required(self, attr, entry):
|
||||
+ """
|
||||
+ Check if an attribute is required in this entry
|
||||
+ """
|
||||
+ entry_oc = entry['objectClass']
|
||||
+ for oc in entry_oc:
|
||||
+ required_attrs = self.local_conn.get_allowed_attributes(
|
||||
+ [oc], raise_on_unknown=False, attributes="must")
|
||||
+ if attr.lower() in required_attrs:
|
||||
+ return True
|
||||
+ return False
|
||||
+
|
||||
def clean_entry(self, entry_dn, entry_type, entry_attrs):
|
||||
"""
|
||||
Clean up the entry from the remote server
|
||||
@@ -1311,7 +1331,17 @@ class IPAMigrate():
|
||||
f"'{old_value}' "
|
||||
"new value "
|
||||
f"'{local_entry[attr][0]}'")
|
||||
-
|
||||
+ elif 'single' == sp_attr[1]:
|
||||
+ # The attribute is defined as multivalued, but
|
||||
+ # we really need to treat it as single valued
|
||||
+ self.log_debug("Entry is different and will "
|
||||
+ f"be updated: '{local_dn}' "
|
||||
+ f"attribute '{attr}' replaced "
|
||||
+ "with val "
|
||||
+ f"'{remote_attrs[attr][0]}' "
|
||||
+ "old value: "
|
||||
+ f"{local_entry[attr][0]}")
|
||||
+ local_entry[attr][0] = remote_attrs[attr][0]
|
||||
goto_next_attr = True
|
||||
break
|
||||
|
||||
@@ -1358,6 +1388,31 @@ class IPAMigrate():
|
||||
local_entry[attr] = remote_attrs[attr]
|
||||
entry_updated = True
|
||||
|
||||
+ # Remove attributes in the local entry that do not exist in the
|
||||
+ # remote entry
|
||||
+ remove_attrs = []
|
||||
+ for attr in local_entry:
|
||||
+ if (self.attr_is_operational(attr)
|
||||
+ and attr.lower() not in POLICY_OP_ATTRS) or \
|
||||
+ attr.lower() in IGNORE_ATTRS or \
|
||||
+ attr.lower() in STRIP_ATTRS or \
|
||||
+ attr.lower() == "usercertificate":
|
||||
+ # This is an attribute that we do not want to remove
|
||||
+ continue
|
||||
+
|
||||
+ if attr not in remote_attrs and \
|
||||
+ not self.attr_is_required(attr, local_entry):
|
||||
+ # Mark this attribute for deletion
|
||||
+ remove_attrs.append(attr)
|
||||
+ entry_updated = True
|
||||
+
|
||||
+ # Remove attributes
|
||||
+ for remove_attr in remove_attrs:
|
||||
+ self.log_debug("Entry is different and will be updated: "
|
||||
+ f"'{local_dn}' attribute '{remove_attr}' "
|
||||
+ "is being removed")
|
||||
+ del local_entry[remove_attr]
|
||||
+
|
||||
if range_reset:
|
||||
stats['reset_range'] += 1
|
||||
|
||||
@@ -1371,6 +1426,9 @@ class IPAMigrate():
|
||||
"""
|
||||
Process chunks of remote entries from a paged results search
|
||||
|
||||
+ entry_dn = the remote entry DN
|
||||
+ entry_attrs = the remote entry's attributes stored in a dict
|
||||
+
|
||||
Identify entry type
|
||||
Process entry (removing/change attr/val/schema)
|
||||
Compare processed remote entry with local entry, merge/overwrite?
|
||||
@@ -1426,6 +1484,47 @@ class IPAMigrate():
|
||||
# Based on the entry type do additional work
|
||||
#
|
||||
|
||||
+ # For entries with alternate identifying needs we need to rebuild the
|
||||
+ # local dn. Typically this is for entries that use ipaUniqueId as the
|
||||
+ # RDN attr
|
||||
+ if entry_type != "custom" and 'alt_id' in DB_OBJECTS[entry_type]:
|
||||
+ attr = DB_OBJECTS[entry_type]['alt_id']['attr']
|
||||
+ base = DB_OBJECTS[entry_type]['alt_id']['base']
|
||||
+ srch_filter = f'{attr}={entry_attrs[attr][0]}'
|
||||
+ if DB_OBJECTS[entry_type]['alt_id']['isDN'] is True:
|
||||
+ # Convert the filter to match the local suffix
|
||||
+ srch_filter = self.replace_suffix(srch_filter)
|
||||
+ srch_base = base + str(self.local_suffix)
|
||||
+
|
||||
+ try:
|
||||
+ entries = self.local_conn.get_entries(DN(srch_base),
|
||||
+ filter=srch_filter)
|
||||
+ if len(entries) == 1:
|
||||
+ local_dn = entries[0].dn
|
||||
+ elif len(entries) == 0:
|
||||
+ # Not found, no problem just proceed and we will add it
|
||||
+ pass
|
||||
+ else:
|
||||
+ # Found too many entries - should not happen
|
||||
+ self.log_error('Found too many local matching entries '
|
||||
+ f'for "{local_dn}"')
|
||||
+ if self.args.force:
|
||||
+ stats['ignored_errors'] += 1
|
||||
+ return
|
||||
+ else:
|
||||
+ sys.exit(1)
|
||||
+ except errors.EmptyResult:
|
||||
+ # Not found, no problem just proceed and we will add it later
|
||||
+ pass
|
||||
+ except (errors.NetworkError, errors.DatabaseError) as e:
|
||||
+ self.log_error('Failed to find a local matching entry for '
|
||||
+ f'"{local_dn}" error: {str(e)}')
|
||||
+ if self.args.force:
|
||||
+ stats['ignored_errors'] += 1
|
||||
+ return
|
||||
+ else:
|
||||
+ sys.exit(1)
|
||||
+
|
||||
# See if the entry exists on the local server
|
||||
try:
|
||||
local_entry = self.local_conn.get_entry(DN(local_dn),
|
||||
@@ -1441,14 +1540,20 @@ class IPAMigrate():
|
||||
|
||||
if self.dryrun:
|
||||
self.write_update_to_ldif(local_entry)
|
||||
- DB_OBJECTS[entry_type]['count'] += 1
|
||||
+ if entry_type == "custom":
|
||||
+ stats['custom'] += 1
|
||||
+ else:
|
||||
+ DB_OBJECTS[entry_type]['count'] += 1
|
||||
stats['total_db_migrated'] += 1
|
||||
return
|
||||
|
||||
# Update the local entry
|
||||
try:
|
||||
self.local_conn.update_entry(local_entry)
|
||||
- DB_OBJECTS[entry_type]['count'] += 1
|
||||
+ if entry_type == "custom":
|
||||
+ stats['custom'] += 1
|
||||
+ else:
|
||||
+ DB_OBJECTS[entry_type]['count'] += 1
|
||||
except errors.ExecutionError as e:
|
||||
self.log_error(f'Failed to update "{local_dn}" error: '
|
||||
f'{str(e)}')
|
||||
@@ -1567,7 +1672,7 @@ class IPAMigrate():
|
||||
"""
|
||||
Used paged search for online method to avoid large memory footprint
|
||||
"""
|
||||
- self.log_info("Migrating database ... (this make take a while)")
|
||||
+ self.log_info("Migrating database ... (this may take a while)")
|
||||
if self.args.db_ldif is not None:
|
||||
self.processDBOffline()
|
||||
else:
|
||||
@@ -1608,7 +1713,7 @@ class IPAMigrate():
|
||||
f"{len(objectclasses)} objectClasses")
|
||||
|
||||
# Loop over attributes and objectclasses and count them
|
||||
- schema = self.local_conn._get_schema()
|
||||
+ schema = self.local_conn.schema
|
||||
local_schema = schema.ldap_entry()
|
||||
for schema_type in [(attributes, "attributeTypes"),
|
||||
(objectclasses, "objectClasses")]:
|
||||
@@ -1967,7 +2072,7 @@ class IPAMigrate():
|
||||
|
||||
# Run ipa-server-upgrade
|
||||
self.log_info("Running ipa-server-upgrade ... "
|
||||
- "(this make take a while)")
|
||||
+ "(this may take a while)")
|
||||
if self.dryrun:
|
||||
self.log_info("Skipping ipa-server-upgrade in dryrun mode.")
|
||||
else:
|
||||
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
|
||||
index 0e26c75497b216f09ed450aa25a09c2102582326..250f1b5b01bf066d316a98489ab6153b89615173 100644
|
||||
--- a/ipaserver/install/ipa_migrate_constants.py
|
||||
+++ b/ipaserver/install/ipa_migrate_constants.py
|
||||
@@ -19,6 +19,28 @@ STRIP_OP_ATTRS = [
|
||||
'nsuniqueid',
|
||||
'dsentrydn',
|
||||
'entryuuid',
|
||||
+ 'entrydn',
|
||||
+ 'entryid',
|
||||
+ 'entryusn',
|
||||
+ 'numsubordinates',
|
||||
+ 'parentid',
|
||||
+ 'tombstonenumsubordinates'
|
||||
+]
|
||||
+
|
||||
+# Operational attributes that we would want to remove from the local entry if
|
||||
+# they don't exist in the remote entry
|
||||
+POLICY_OP_ATTRS = [
|
||||
+ 'nsaccountlock',
|
||||
+ 'passwordexpiratontime',
|
||||
+ 'passwordgraceusertime',
|
||||
+ 'pwdpolicysubentry',
|
||||
+ 'passwordexpwarned',
|
||||
+ 'passwordretrycount',
|
||||
+ 'retrycountresettime',
|
||||
+ 'accountunlocktime',
|
||||
+ 'passwordhistory',
|
||||
+ 'passwordallowchangetime',
|
||||
+ 'pwdreset'
|
||||
]
|
||||
|
||||
# Atributes to strip from users/groups
|
||||
@@ -110,7 +132,7 @@ STRIP_OC = [
|
||||
#
|
||||
# The DS_CONFIG mapping breaks each config entry (or type of entry) into its
|
||||
# own catagory. Each catagory, or type, as DN list "dn", the attributes# we
|
||||
-# are intrested in. These attributes are broken into singel valued "attrs",
|
||||
+# are intrested in. These attributes are broken into single valued "attrs",
|
||||
# or multi-valued attributes "multivalued". If the attributes is single
|
||||
# valued then the value is replaced, if it's multivalued then it is "appended"
|
||||
#
|
||||
@@ -503,7 +525,7 @@ DS_CONFIG = {
|
||||
}
|
||||
|
||||
#
|
||||
-# Slpai NIS is an optional plugin. It requires special handling
|
||||
+# Slapi NIS is an optional plugin. It requires special handling
|
||||
#
|
||||
NIS_PLUGIN = {
|
||||
'dn': 'cn=NIS Server,cn=plugins,cn=config',
|
||||
@@ -565,6 +587,12 @@ DS_INDEXES = {
|
||||
# identify the entry.
|
||||
# The "label" and "count" attributes are used for the Summary Report
|
||||
#
|
||||
+# Some entries use ipaUniqueId as the RDN attribute, this makes comparing
|
||||
+# entries between the remote and local servers problematic. So we need special
|
||||
+# identifying information to find the local entry. In this case we use the
|
||||
+# "alt_id" key which is a dict of an attribute 'attr' and partial base DN
|
||||
+# 'base' - which is expected to end in a comma.
|
||||
+#
|
||||
DB_OBJECTS = {
|
||||
# Plugins
|
||||
'automember_def': {
|
||||
@@ -640,8 +668,8 @@ DB_OBJECTS = {
|
||||
'oc': ['ipaconfigobject', 'ipaguiconfig'],
|
||||
'subtree': 'cn=ipaconfig,cn=etc,$SUFFIX',
|
||||
'special_attrs': [
|
||||
- # needs special handling, but
|
||||
- # ipa-server-upgrade rewrites this attribute anyway!
|
||||
+ # needs special handling, but ipa-server-upgrade rewrites this
|
||||
+ # attribute anyway!
|
||||
('ipausersearchfields', 'list'),
|
||||
],
|
||||
'label': 'IPA Config',
|
||||
@@ -772,11 +800,16 @@ DB_OBJECTS = {
|
||||
'mode': 'all',
|
||||
'count': 0,
|
||||
},
|
||||
- 'subids': { # unknown what these entries look like TODO
|
||||
+ 'subids': {
|
||||
'oc': [],
|
||||
'subtree': ',cn=subids,cn=accounts,$SUFFIX',
|
||||
'label': 'Sub IDs',
|
||||
- 'mode': 'all', # TODO Maybe production only?
|
||||
+ 'mode': 'production',
|
||||
+ 'alt_id': {
|
||||
+ 'attr': 'ipaOwner',
|
||||
+ 'isDN': True,
|
||||
+ 'base': 'cn=subids,cn=accounts,',
|
||||
+ },
|
||||
'count': 0,
|
||||
},
|
||||
|
||||
@@ -884,6 +917,11 @@ DB_OBJECTS = {
|
||||
'oc': ['ipahbacrule'],
|
||||
'subtree': ',cn=hbac,$SUFFIX',
|
||||
'label': 'HBAC Rules',
|
||||
+ 'alt_id': {
|
||||
+ 'attr': 'cn',
|
||||
+ 'base': 'cn=hbac,',
|
||||
+ 'isDN': False,
|
||||
+ },
|
||||
'mode': 'all',
|
||||
'count': 0,
|
||||
},
|
||||
@@ -892,6 +930,11 @@ DB_OBJECTS = {
|
||||
'selinux_usermap': { # Not sure if this is needed, entry is empty TODO
|
||||
'oc': [],
|
||||
'subtree': ',cn=usermap,cn=selinux,$SUFFIX',
|
||||
+ 'alt_id': {
|
||||
+ 'attr': 'cn',
|
||||
+ 'base': 'cn=usermap,cn=selinux,',
|
||||
+ 'isDN': False,
|
||||
+ },
|
||||
'label': 'Selinux Usermaps',
|
||||
'mode': 'all',
|
||||
'count': 0,
|
||||
@@ -902,12 +945,27 @@ DB_OBJECTS = {
|
||||
'oc': ['ipasudorule'],
|
||||
'subtree': ',cn=sudorules,cn=sudo,$SUFFIX',
|
||||
'label': 'Sudo Rules',
|
||||
+ 'alt_id': {
|
||||
+ 'attr': 'cn',
|
||||
+ 'base': 'cn=sudorules,cn=sudo,',
|
||||
+ 'isDN': False,
|
||||
+ },
|
||||
+ 'special_attrs': [
|
||||
+ # schema defines sudoOrder as mutlivalued, but we need to treat
|
||||
+ # it as single valued
|
||||
+ ('sudoorder', 'single'),
|
||||
+ ],
|
||||
'mode': 'all',
|
||||
'count': 0,
|
||||
},
|
||||
'sudo_cmds': {
|
||||
'oc': ['ipasudocmd'],
|
||||
'subtree': ',cn=sudocmds,cn=sudo,$SUFFIX',
|
||||
+ 'alt_id': {
|
||||
+ 'attr': 'sudoCmd',
|
||||
+ 'base': 'cn=sudocmds,cn=sudo,',
|
||||
+ 'isDN': False,
|
||||
+ },
|
||||
'label': 'Sudo Commands',
|
||||
'mode': 'all',
|
||||
'count': 0,
|
||||
@@ -991,6 +1049,11 @@ DB_OBJECTS = {
|
||||
'oc': ['ipanisnetgroup'],
|
||||
'not_oc': ['mepmanagedentry'],
|
||||
'subtree': ',cn=ng,cn=alt,$SUFFIX',
|
||||
+ 'alt_id': {
|
||||
+ 'attr': 'cn',
|
||||
+ 'base': 'cn=ng,cn=alt,',
|
||||
+ 'isDN': False,
|
||||
+ },
|
||||
'label': 'Network Groups',
|
||||
'mode': 'all',
|
||||
'count': 0,
|
||||
@@ -1006,9 +1069,14 @@ DB_OBJECTS = {
|
||||
'count': 0,
|
||||
},
|
||||
'caacls': {
|
||||
- 'oc': ['top'],
|
||||
+ 'oc': ['ipacaacl'],
|
||||
'subtree': ',cn=caacls,cn=ca,$SUFFIX',
|
||||
- 'label': 'CA Certificates',
|
||||
+ 'alt_id': {
|
||||
+ 'attr': 'cn',
|
||||
+ 'base': 'cn=caacls,cn=ca,',
|
||||
+ 'isDN': False,
|
||||
+ },
|
||||
+ 'label': 'CA Certificate ACLs',
|
||||
'mode': 'all',
|
||||
'count': 0,
|
||||
},
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From b2c83c462aba3d7367f01f665f14b126b7e74b9e Mon Sep 17 00:00:00 2001
|
||||
From aa90ce09c3f6d496ee2cf63123c619f1008140b4 Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Wed, 31 Dec 2025 12:15:51 +0530
|
||||
Subject: [PATCH] ipatests: Add DNS functional integration tests.
|
||||
@ -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
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
From 3b5a980f5b65b03b9fd7ad0cfbb6c87874d3ff24 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Tue, 3 Sep 2024 13:42:05 -0400
|
||||
Subject: [PATCH] ipa-migrate - fix alternate entry search filter
|
||||
|
||||
Processing a filter like a DN can cause normalization issues that result
|
||||
in an invalid filter. Make sure the filter is encapsulated with
|
||||
parenthesis and we call replace_suffix_value() instead of
|
||||
replace_suffix()
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9658
|
||||
|
||||
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
|
||||
|
||||
Fix typo in test
|
||||
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipaserver/install/ipa_migrate.py | 4 ++--
|
||||
ipatests/test_integration/test_ipa_ipa_migration.py | 6 +++---
|
||||
2 files changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
|
||||
index 78c530f24fe5d8c9f5de0f816df9904bf30c7b94..38356aa23ea435e2a616f48356feaea7b50dd1e4 100644
|
||||
--- a/ipaserver/install/ipa_migrate.py
|
||||
+++ b/ipaserver/install/ipa_migrate.py
|
||||
@@ -1490,10 +1490,10 @@ class IPAMigrate():
|
||||
if entry_type != "custom" and 'alt_id' in DB_OBJECTS[entry_type]:
|
||||
attr = DB_OBJECTS[entry_type]['alt_id']['attr']
|
||||
base = DB_OBJECTS[entry_type]['alt_id']['base']
|
||||
- srch_filter = f'{attr}={entry_attrs[attr][0]}'
|
||||
+ srch_filter = f'({attr}={entry_attrs[attr][0]})'
|
||||
if DB_OBJECTS[entry_type]['alt_id']['isDN'] is True:
|
||||
# Convert the filter to match the local suffix
|
||||
- srch_filter = self.replace_suffix(srch_filter)
|
||||
+ srch_filter = self.replace_suffix_value(srch_filter)
|
||||
srch_base = base + str(self.local_suffix)
|
||||
|
||||
try:
|
||||
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
index f697bbfbfc6169309274db689501c99fe148cc70..288165e8a83a96e6f6bd4e52866f98617f497c56 100644
|
||||
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
@@ -610,7 +610,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
|
||||
MIGRATION_CONFIG_LOG_MSG = "Migrating configuration ...\n"
|
||||
IPA_UPGRADE_LOG_MSG = (
|
||||
- "Running ipa-server-upgrade ... (this make take a while)\n"
|
||||
+ "Running ipa-server-upgrade ... (this may take a while)\n"
|
||||
)
|
||||
SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
|
||||
MIGRATION_COMPLETE_LOG_MSG = "Migration complete!\n"
|
||||
@@ -641,10 +641,10 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
tasks.kinit_admin(self.replicas[0])
|
||||
MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
|
||||
MIGRATION_DATABASE_LOG_MSG = (
|
||||
- "Migrating database ... (this make take a while)\n"
|
||||
+ "Migrating database ... (this may take a while)\n"
|
||||
)
|
||||
IPA_UPGRADE_LOG_MSG = (
|
||||
- "Running ipa-server-upgrade ... (this make take a while)\n"
|
||||
+ "Running ipa-server-upgrade ... (this may take a while)\n"
|
||||
)
|
||||
SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
|
||||
result = run_migrate(
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
From a343c149838a3058794f33c75c58b75bc1748f7f Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 17 Sep 2024 17:00:49 +0200
|
||||
Subject: [PATCH] ipatests: provide a ccache to rpcclient deletetrustdom
|
||||
|
||||
With samba update to samba-4.20.4, rpcclient now needs a
|
||||
ccache otherwise it prompts for a password.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9667
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipatests/pytest_ipa/integration/tasks.py | 23 ++++++++++++++++++++---
|
||||
1 file changed, 20 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index 9d6b5f67a311a28c335801d59e0ff0f0c7faccdd..677fb7534256a65940fb5280fa6412789dcba54f 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -795,15 +795,22 @@ def remove_trust_info_from_ad(master, ad_domain, ad_hostname):
|
||||
kinit_as_user(master,
|
||||
'Administrator@{}'.format(ad_domain.upper()),
|
||||
master.config.ad_admin_password)
|
||||
+ # Find cache for the user
|
||||
+ cache_args = []
|
||||
+ cache = get_credential_cache(master)
|
||||
+ if cache:
|
||||
+ cache_args = ["--use-krb5-ccache", cache]
|
||||
+
|
||||
# Detect whether rpcclient supports -k or --use-kerberos option
|
||||
res = master.run_command(['rpcclient', '-h'], raiseonerr=False)
|
||||
if "--use-kerberos" in res.stderr_text:
|
||||
rpcclient_krb5_knob = "--use-kerberos=desired"
|
||||
else:
|
||||
rpcclient_krb5_knob = "-k"
|
||||
- master.run_command(['rpcclient', rpcclient_krb5_knob, ad_hostname,
|
||||
- '-c', 'deletetrustdom {}'.format(master.domain.name)],
|
||||
- raiseonerr=False)
|
||||
+ cmd_args = ['rpcclient', rpcclient_krb5_knob, ad_hostname]
|
||||
+ cmd_args.extend(cache_args)
|
||||
+ cmd_args.extend(['-c', 'deletetrustdom {}'.format(master.domain.name)])
|
||||
+ master.run_command(cmd_args, raiseonerr=False)
|
||||
|
||||
|
||||
def configure_auth_to_local_rule(master, ad):
|
||||
@@ -1086,6 +1093,16 @@ def kinit_admin(host, raiseonerr=True):
|
||||
raiseonerr=raiseonerr)
|
||||
|
||||
|
||||
+def get_credential_cache(host):
|
||||
+ # Return the credential cache currently in use on host or None
|
||||
+ result = host.run_command(["klist"]).stdout_text
|
||||
+ pattern = re.compile(r'Ticket cache: (?P<cache>.*)\n')
|
||||
+ res = pattern.search(result)
|
||||
+ if res:
|
||||
+ return res['cache']
|
||||
+ return None
|
||||
+
|
||||
+
|
||||
def uninstall_master(host, ignore_topology_disconnect=True,
|
||||
ignore_last_of_role=True, clean=True, verbose=False):
|
||||
uninstall_cmd = ['ipa-server-install', '--uninstall', '-U']
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
From 743c7b46e463bef666dc84e9f513eb7dee7f59f6 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 17 Sep 2024 14:48:58 +0200
|
||||
Subject: [PATCH] test_adtrust_install: add --use-krb5-ccache to smbclient
|
||||
command
|
||||
|
||||
With samba 4.20.4 the smbclient commands needs a ccache otherwise it
|
||||
prompts for a password.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9666
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
.../test_integration/test_adtrust_install.py | 28 ++++++++++++-------
|
||||
1 file changed, 18 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
|
||||
index de252db1705ad940c3b5ee4df967d7c17a4203a7..79a91dfaa61276de74b10777c6d44b5942ed1be0 100644
|
||||
--- a/ipatests/test_integration/test_adtrust_install.py
|
||||
+++ b/ipatests/test_integration/test_adtrust_install.py
|
||||
@@ -873,17 +873,25 @@ class TestIpaAdTrustInstall(IntegrationTest):
|
||||
"path", "/freeipa4234"])
|
||||
self.master.run_command(["touch", "before"])
|
||||
self.master.run_command(["touch", "after"])
|
||||
- self.master.run_command(
|
||||
- ["smbclient", "--use-kerberos=desired",
|
||||
- "-c=put before", "//{}/share".format(
|
||||
- self.master.hostname)]
|
||||
- )
|
||||
+ # Find cache for the admin user
|
||||
+ cache_args = []
|
||||
+ cache = tasks.get_credential_cache(self.master)
|
||||
+ if cache:
|
||||
+ cache_args = ["--use-krb5-ccache", cache]
|
||||
+
|
||||
+ cmd_args = ["smbclient", "--use-kerberos=desired"]
|
||||
+ cmd_args.extend(cache_args)
|
||||
+ cmd_args.extend([
|
||||
+ "-c=put before", "//{}/share".format(self.master.hostname)
|
||||
+ ])
|
||||
+ self.master.run_command(cmd_args)
|
||||
self.master.run_command(
|
||||
["net", "conf", "setparm", "share",
|
||||
"valid users", "@admins"])
|
||||
- result = self.master.run_command(
|
||||
- ["smbclient", "--use-kerberos=desired",
|
||||
- "-c=put after", "//{}/share".format(
|
||||
- self.master.hostname)]
|
||||
- )
|
||||
+ cmd_args = ["smbclient", "--use-kerberos=desired"]
|
||||
+ cmd_args.extend(cache_args)
|
||||
+ cmd_args.extend([
|
||||
+ "-c=put after", "//{}/share".format(self.master.hostname)
|
||||
+ ])
|
||||
+ result = self.master.run_command(cmd_args)
|
||||
assert msg not in result.stdout_text
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -1,209 +0,0 @@
|
||||
From a785d0c561b8e22bd9d56739481095e07e0a7eb7 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Mon, 30 Sep 2024 13:30:46 -0400
|
||||
Subject: [PATCH] Don't rely on removing the CA to uninstall the ACME depoyment
|
||||
|
||||
There has always been a pki-server commnd acme-remove. We were
|
||||
not aware that it should be called prior to removing a CA. In
|
||||
11.5.0 this is strongly encouraged by the PKI team. In 11.6.0
|
||||
ACME is treated as a full subsystem so will be removed in the
|
||||
future using pkidestroy -s ACME
|
||||
|
||||
The new class acmeinstance.ACMEInstance is introduced so its
|
||||
uninstallation can be handled in a similar way as the other
|
||||
PKI services via DogtagInstance. It is, right now, a pretty
|
||||
thin wrapper.
|
||||
|
||||
We can discuss moving the ACME installation routines here at
|
||||
some point. It would be ok as long as we don't have to introduce
|
||||
another PKI restart as part of it.
|
||||
|
||||
In PKI 11.6.0 pkidestroy has new options to ensure a clean
|
||||
uninstall: --remove-conf --remove-logs. Pass those options
|
||||
into pkidestroy calls for 11.6.0+.
|
||||
|
||||
Clean up an additional IPA-generated file that needs to be
|
||||
cleaned up during uninstall: /root/kracert.p12. 11.6.0 is
|
||||
more sensitive to leftover files than previous versions.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9673
|
||||
Fixes: https://pagure.io/freeipa/issue/9674
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipaserver/install/acmeinstance.py | 31 +++++++++++++
|
||||
ipaserver/install/ca.py | 5 ++-
|
||||
ipaserver/install/cainstance.py | 1 +
|
||||
ipaserver/install/dogtaginstance.py | 44 +++++++++++++------
|
||||
.../test_integration/test_uninstallation.py | 21 +++++++++
|
||||
5 files changed, 87 insertions(+), 15 deletions(-)
|
||||
create mode 100644 ipaserver/install/acmeinstance.py
|
||||
|
||||
diff --git a/ipaserver/install/acmeinstance.py b/ipaserver/install/acmeinstance.py
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0027c314545f384d9b6ee24b279479e5360d8bef
|
||||
--- /dev/null
|
||||
+++ b/ipaserver/install/acmeinstance.py
|
||||
@@ -0,0 +1,31 @@
|
||||
+#
|
||||
+# Copyright (C) 2024 FreeIPA Contributors see COPYING for license
|
||||
+#
|
||||
+
|
||||
+import logging
|
||||
+
|
||||
+from ipaserver.install.dogtaginstance import DogtagInstance
|
||||
+
|
||||
+logger = logging.getLogger(__name__)
|
||||
+
|
||||
+
|
||||
+class ACMEInstance(DogtagInstance):
|
||||
+ """
|
||||
+ ACME is deployed automatically with a CA subsystem but it is the
|
||||
+ responsibility of IPA to uninstall the service.
|
||||
+
|
||||
+ This is mostly a placeholder for the uninstaller. We can
|
||||
+ eventually move the ACME installation routines into this class
|
||||
+ if we want but it might result in an extra PKI restart which
|
||||
+ would be slow.
|
||||
+ """
|
||||
+ def __init__(self, realm=None, host_name=None):
|
||||
+ super(ACMEInstance, self).__init__(
|
||||
+ realm=realm,
|
||||
+ subsystem="ACME",
|
||||
+ service_desc="ACME server",
|
||||
+ host_name=host_name
|
||||
+ )
|
||||
+
|
||||
+ def uninstall(self):
|
||||
+ DogtagInstance.uninstall(self)
|
||||
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
|
||||
index ffcb5268399ce71128fc8de5f54d433d35e99dd2..520e3fc5de1084e7c22c0cf7eaa86e1d3c421373 100644
|
||||
--- a/ipaserver/install/ca.py
|
||||
+++ b/ipaserver/install/ca.py
|
||||
@@ -22,7 +22,7 @@ from ipaplatform.constants import constants
|
||||
from ipaserver.install import sysupgrade
|
||||
from ipapython.install import typing
|
||||
from ipapython.install.core import group, knob, extend_knob
|
||||
-from ipaserver.install import cainstance, bindinstance, dsinstance
|
||||
+from ipaserver.install import acmeinstance, cainstance, bindinstance, dsinstance
|
||||
from ipapython import ipautil, certdb
|
||||
from ipapython import ipaldap
|
||||
from ipapython.admintool import ScriptError
|
||||
@@ -715,6 +715,9 @@ def install_step_1(standalone, replica_config, options, custodia):
|
||||
|
||||
|
||||
def uninstall():
|
||||
+ acme = acmeinstance.ACMEInstance(api.env.realm)
|
||||
+ acme.uninstall()
|
||||
+
|
||||
ca_instance = cainstance.CAInstance(api.env.realm)
|
||||
ca_instance.stop_tracking_certificates()
|
||||
ipautil.remove_file(paths.RA_AGENT_PEM)
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index 5dac2c0441752e7bb569cde1fc93bc17c3128cdf..5c2c9f8b981cf5d587865f7680e2b231eae655e2 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -1118,6 +1118,7 @@ class CAInstance(DogtagInstance):
|
||||
|
||||
ipautil.remove_file(paths.DOGTAG_ADMIN_P12)
|
||||
ipautil.remove_file(paths.CACERT_P12)
|
||||
+ ipautil.remove_file(paths.ADMIN_CERT_PATH)
|
||||
|
||||
def unconfigure_certmonger_renewal_guard(self):
|
||||
if not self.is_configured():
|
||||
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
|
||||
index e89492312deb8ca20668a62fd7a2a20e2866a3fb..4b0f4d274b0c33140ed6f939f1a3fd8b75930ff9 100644
|
||||
--- a/ipaserver/install/dogtaginstance.py
|
||||
+++ b/ipaserver/install/dogtaginstance.py
|
||||
@@ -304,21 +304,37 @@ class DogtagInstance(service.Service):
|
||||
if self.is_installed():
|
||||
self.print_msg("Unconfiguring %s" % self.subsystem)
|
||||
|
||||
- args = [paths.PKIDESTROY,
|
||||
- "-i", "pki-tomcat", "--force",
|
||||
- "-s", self.subsystem]
|
||||
-
|
||||
- # specify --log-file <path> on PKI 11.0.0 or later
|
||||
-
|
||||
+ args = []
|
||||
pki_version = pki.util.Version(pki.specification_version())
|
||||
- if pki_version >= pki.util.Version("11.0.0"):
|
||||
- timestamp = time.strftime(
|
||||
- "%Y%m%d%H%M%S",
|
||||
- time.localtime(time.time()))
|
||||
- log_file = os.path.join(
|
||||
- paths.VAR_LOG_PKI_DIR,
|
||||
- "pki-%s-destroy.%s.log" % (self.subsystem.lower(), timestamp))
|
||||
- args.extend(["--log-file", log_file])
|
||||
+ if self.subsystem == "ACME":
|
||||
+ if pki_version < pki.util.Version("11.0.0"):
|
||||
+ return
|
||||
+ elif (
|
||||
+ pki.util.Version("11.0.0") <= pki_version
|
||||
+ <= pki.util.Version("11.5.0")
|
||||
+ ):
|
||||
+ args = ['pki-server', 'acme-remove']
|
||||
+ else:
|
||||
+ # fall through for PKI >= 11.6.0
|
||||
+ pass
|
||||
+ if not args:
|
||||
+ args = [paths.PKIDESTROY,
|
||||
+ "-i", "pki-tomcat", "--force",
|
||||
+ "-s", self.subsystem]
|
||||
+
|
||||
+ # specify --log-file <path> on PKI 11.0.0 or later
|
||||
+
|
||||
+ if pki_version >= pki.util.Version("11.0.0"):
|
||||
+ timestamp = time.strftime(
|
||||
+ "%Y%m%d%H%M%S",
|
||||
+ time.localtime(time.time()))
|
||||
+ log_file = os.path.join(
|
||||
+ paths.VAR_LOG_PKI_DIR,
|
||||
+ "pki-%s-destroy.%s.log" %
|
||||
+ (self.subsystem.lower(), timestamp))
|
||||
+ args.extend(["--log-file", log_file])
|
||||
+ if pki_version >= pki.util.Version("11.6.0"):
|
||||
+ args.extend(["--remove-conf", "--remove-logs"])
|
||||
|
||||
try:
|
||||
ipautil.run(args)
|
||||
diff --git a/ipatests/test_integration/test_uninstallation.py b/ipatests/test_integration/test_uninstallation.py
|
||||
index 4f8f17ce3ad8d5376ecba11442f379e5691de7f7..049c50db536ae1070f5f958e76b12a1518da0aba 100644
|
||||
--- a/ipatests/test_integration/test_uninstallation.py
|
||||
+++ b/ipatests/test_integration/test_uninstallation.py
|
||||
@@ -197,6 +197,7 @@ class TestUninstallCleanup(IntegrationTest):
|
||||
'/var/lib/sss/pubconf/krb5.include.d/localauth_plugin',
|
||||
'/var/named/dynamic/managed-keys.bind',
|
||||
'/var/named/dynamic/managed-keys.bind.jnl',
|
||||
+ '/var/lib/systemd/coredump/',
|
||||
]
|
||||
|
||||
leftovers = []
|
||||
@@ -217,3 +218,23 @@ class TestUninstallCleanup(IntegrationTest):
|
||||
leftovers.append(line)
|
||||
|
||||
assert len(leftovers) == 0
|
||||
+
|
||||
+
|
||||
+class TestUninstallReinstall(IntegrationTest):
|
||||
+ """Test install, uninstall, re-install.
|
||||
+
|
||||
+ Reinstall with PKI 11.6.0 was failing
|
||||
+ https://pagure.io/freeipa/issue/9673
|
||||
+ """
|
||||
+
|
||||
+ num_replicas = 0
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ tasks.install_master(cls.master, setup_dns=False)
|
||||
+
|
||||
+ def test_uninstall_server(self):
|
||||
+ tasks.uninstall_master(self.master)
|
||||
+
|
||||
+ def test_reinstall_server(self):
|
||||
+ tasks.install_master(self.master, setup_dns=False)
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From 55b01956676add56e1660b23317107f3010fdf5d Mon Sep 17 00:00:00 2001
|
||||
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
|
||||
@ -17,19 +17,70 @@ 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 +++++++++++++++++++
|
||||
2 files changed, 102 insertions(+)
|
||||
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 561e021..c7b4f97 100755
|
||||
index 32ac5cbc2c6fe87850dfb15c1d5beae6fa648dfb..ff2ea9792d04ebd2e6bd7bb3b51d97f35cb3fbfb 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -3260,3 +3260,18 @@ def check_journal_does_not_contain_secret(host, cmd):
|
||||
result = host.run_command(journalctl_cmd, raiseonerr=False)
|
||||
assert (host.config.admin_password not in result.stdout_text)
|
||||
assert (host.config.dirman_password not in result.stdout_text)
|
||||
@@ -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):
|
||||
@ -46,10 +97,10 @@ index 561e021..c7b4f97 100755
|
||||
+ 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 3c67833..7859b56 100644
|
||||
index 76d6aa24e2ab3d88b7013e0d107d0e27ae7f3426..f8c8414eefbc015cfc0947de575ea349a65a5e73 100644
|
||||
--- a/ipatests/test_integration/test_replica_promotion.py
|
||||
+++ b/ipatests/test_integration/test_replica_promotion.py
|
||||
@@ -1367,3 +1367,90 @@ class TestReplicaConn(IntegrationTest):
|
||||
@@ -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
|
||||
@ -1,4 +1,4 @@
|
||||
From 05198e8fcf3410d4f8a97e8c6282cbabef193980 Mon Sep 17 00:00:00 2001
|
||||
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
|
||||
@ -9,11 +9,75 @@ and bindpw options with positive and negative scenarios.
|
||||
Related: https://pagure.io/freeipa/issue/9930
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/pytest_ipa/integration/tasks.py | 49 ++
|
||||
ipatests/test_integration/test_ipa_join.py | 614 +++++++++++++++++++++
|
||||
2 files changed, 663 insertions(+)
|
||||
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
|
||||
@ -1,35 +0,0 @@
|
||||
From ae4c2ad6cd966d48c063814f494dcc16cf0ccd4c Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Tue, 24 Sep 2024 13:46:48 +0530
|
||||
Subject: [PATCH] ipatests: Fixes for ipa-idrange-fix testsuite
|
||||
|
||||
This patch adds the line tasks.install_master(cls.master).
|
||||
The kinit admin command fails with the below error as the
|
||||
IPA is not configured on the test system
|
||||
|
||||
'ipa: ERROR: stderr: kinit: Configuration file does not specify default
|
||||
realm when parsing name admin'
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_ipa_idrange_fix.py | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipa_idrange_fix.py b/ipatests/test_integration/test_ipa_idrange_fix.py
|
||||
index ff8fbdac9d028d26fc55f5e357f89af879a61723..0c915bd0931ed11a3aa86c533ee8748aa8a7ec07 100644
|
||||
--- a/ipatests/test_integration/test_ipa_idrange_fix.py
|
||||
+++ b/ipatests/test_integration/test_ipa_idrange_fix.py
|
||||
@@ -17,6 +17,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TestIpaIdrangeFix(IntegrationTest):
|
||||
+
|
||||
+ topology = 'line'
|
||||
+
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
super(TestIpaIdrangeFix, cls).install(mh)
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -1,265 +0,0 @@
|
||||
From 18303b94bea4e08a0c889fc357df6ba2f308fa0d Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 2 Oct 2024 21:26:34 -0400
|
||||
Subject: [PATCH] Do not let user with an expired OTP token to log in if only
|
||||
OTP is allowed
|
||||
|
||||
If only OTP authentication is allowed, and a user tries to login with an
|
||||
expired token, do not let them log in with their password. Forcing the
|
||||
admin to intervene. If the user does not have an OTP token then allow
|
||||
them to log in with a password until an OTP token is configured
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9387
|
||||
|
||||
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Julien Rische <jrische@redhat.com>
|
||||
---
|
||||
daemons/ipa-kdb/ipa_kdb_principals.c | 63 +++++++++++--
|
||||
.../ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 3 +-
|
||||
ipatests/test_integration/test_otp.py | 94 ++++++++++++++++++-
|
||||
3 files changed, 151 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
index 14603e528b43acb29234c425e97ad297ac6724a7..114957b884786dd3ca3b01c47f6bb82e8a040beb 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
@@ -107,7 +107,6 @@ static char *std_principal_obj_classes[] = {
|
||||
"krbprincipal",
|
||||
"krbprincipalaux",
|
||||
"krbTicketPolicyAux",
|
||||
-
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -338,14 +337,16 @@ static void ipadb_validate_otp(struct ipadb_context *ipactx,
|
||||
if (dn == NULL)
|
||||
return;
|
||||
count = asprintf(&filter, ftmpl, dn, datetime, datetime);
|
||||
- ldap_memfree(dn);
|
||||
- if (count < 0)
|
||||
+ if (count < 0) {
|
||||
+ ldap_memfree(dn);
|
||||
return;
|
||||
+ }
|
||||
|
||||
/* Fetch the active token list. */
|
||||
kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
|
||||
filter, (char**) attrs, &res);
|
||||
free(filter);
|
||||
+ filter = NULL;
|
||||
if (kerr != 0 || res == NULL)
|
||||
return;
|
||||
|
||||
@@ -353,10 +354,60 @@ static void ipadb_validate_otp(struct ipadb_context *ipactx,
|
||||
count = ldap_count_entries(ipactx->lcontext, res);
|
||||
ldap_msgfree(res);
|
||||
|
||||
- /* If the user is configured for OTP, but has no active tokens, remove
|
||||
- * OTP from the list since the user obviously can't log in this way. */
|
||||
- if (count == 0)
|
||||
+ /*
|
||||
+ * If there are no valid tokens then we need to remove the OTP flag,
|
||||
+ * unless OTP is the only auth type allowed...
|
||||
+ */
|
||||
+ if (count == 0) {
|
||||
+ /* Remove the OTP flag for now */
|
||||
*ua &= ~IPADB_USER_AUTH_OTP;
|
||||
+
|
||||
+ if (*ua == 0) {
|
||||
+ /*
|
||||
+ * Ok, we "only" allow OTP, so if there is an expired/disabled
|
||||
+ * token then add back the OTP flag as the server will double
|
||||
+ * check the validity and reject the entire bind. Otherwise, this
|
||||
+ * is the first time the user is authenticating and the user
|
||||
+ * should be allowed to bind using its password
|
||||
+ */
|
||||
+ static const char *expired_ftmpl = "(&"
|
||||
+ "(objectClass=ipaToken)(ipatokenOwner=%s)"
|
||||
+ "(|(ipatokenNotAfter<=%s)(!(ipatokenNotAfter=*))"
|
||||
+ "(ipatokenDisabled=True))"
|
||||
+ ")";
|
||||
+ if (asprintf(&filter, expired_ftmpl, dn, datetime) < 0) {
|
||||
+ ldap_memfree(dn);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ krb5_klog_syslog(LOG_INFO,
|
||||
+ "Entry (%s) does not have a valid token and only OTP "
|
||||
+ "authentication is supported, checking for expired tokens...",
|
||||
+ dn);
|
||||
+
|
||||
+ kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
|
||||
+ filter, (char**) attrs, &res);
|
||||
+ free(filter);
|
||||
+ if (kerr != 0 || res == NULL) {
|
||||
+ ldap_memfree(dn);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (ldap_count_entries(ipactx->lcontext, res) > 0) {
|
||||
+ /*
|
||||
+ * Ok we only allow OTP, and there are expired/disabled tokens
|
||||
+ * so add the OTP flag back, and the server will reject the
|
||||
+ * bind
|
||||
+ */
|
||||
+ krb5_klog_syslog(LOG_INFO,
|
||||
+ "Entry (%s) does have an expired/disabled token so this "
|
||||
+ "user can not fall through to password auth", dn);
|
||||
+ *ua |= IPADB_USER_AUTH_OTP;
|
||||
+ }
|
||||
+ ldap_msgfree(res);
|
||||
+ }
|
||||
+ }
|
||||
+ ldap_memfree(dn);
|
||||
}
|
||||
|
||||
static void ipadb_validate_radius(struct ipadb_context *ipactx,
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
index c967e2cfffbd920280639f3188783ec150523b47..1c1340e31ac30cb01412a7065ea339cb5461e839 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
@@ -1528,7 +1528,8 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
if (!syncreq && (otpreq == OTP_IS_NOT_REQUIRED)) {
|
||||
ret = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_ONLY_CONFIG);
|
||||
if (ret != 0) {
|
||||
- LOG_FATAL("ipapwd_gen_checks failed!?\n");
|
||||
+ LOG_FATAL("ipapwd_gen_checks failed for '%s': %s\n",
|
||||
+ slapi_sdn_get_dn(sdn), errMesg);
|
||||
slapi_entry_free(entry);
|
||||
slapi_sdn_free(&sdn);
|
||||
return 0;
|
||||
diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py
|
||||
index 350371bfe1e4c1cc6dcc89f6584f813fcb0d32a0..878b4fb560ba8d7768ead54b065656462545babd 100644
|
||||
--- a/ipatests/test_integration/test_otp.py
|
||||
+++ b/ipatests/test_integration/test_otp.py
|
||||
@@ -10,6 +10,7 @@ import re
|
||||
import time
|
||||
import textwrap
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
+from paramiko import AuthenticationException
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
@@ -83,7 +84,7 @@ def kinit_otp(host, user, *, password, otp, success=True):
|
||||
)
|
||||
|
||||
|
||||
-def ssh_2f(hostname, username, answers_dict, port=22):
|
||||
+def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
|
||||
"""
|
||||
:param hostname: hostname
|
||||
:param username: username
|
||||
@@ -103,6 +104,10 @@ def ssh_2f(hostname, username, answers_dict, port=22):
|
||||
logger.info("Prompt is: '%s'", prmpt_str)
|
||||
logger.info(
|
||||
"Answer to ssh prompt is: '%s'", answers_dict[prmpt_str])
|
||||
+ if unwanted_prompt and prmpt_str == unwanted_prompt:
|
||||
+ # We should not see this prompt
|
||||
+ raise ValueError("We got an unwanted prompt: "
|
||||
+ + answers_dict[prmpt_str])
|
||||
return resp
|
||||
|
||||
import paramiko
|
||||
@@ -193,7 +198,8 @@ class TestOTPToken(IntegrationTest):
|
||||
|
||||
# skipping too many OTP fails
|
||||
otp1 = hotp.generate(10).decode("ascii")
|
||||
- kinit_otp(self.master, USER, password=PASSWORD, otp=otp1, success=False)
|
||||
+ kinit_otp(self.master, USER, password=PASSWORD, otp=otp1,
|
||||
+ success=False)
|
||||
# Now the token is desynchronized
|
||||
yield (otpuid, hotp)
|
||||
|
||||
@@ -536,3 +542,87 @@ class TestOTPToken(IntegrationTest):
|
||||
finally:
|
||||
master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '1'])
|
||||
master.run_command(['ipa', 'user-del', USER1])
|
||||
+
|
||||
+ def test_totp_expired_ldap(self):
|
||||
+ master = self.master
|
||||
+ basedn = master.domain.basedn
|
||||
+ USER1 = 'user-expired-otp'
|
||||
+ TMP_PASSWORD = 'Secret1234509'
|
||||
+ binddn = DN(f"uid={USER1},cn=users,cn=accounts,{basedn}")
|
||||
+ controls = [
|
||||
+ BooleanControl(
|
||||
+ controlType="2.16.840.1.113730.3.8.10.7",
|
||||
+ booleanValue=True)
|
||||
+ ]
|
||||
+
|
||||
+ tasks.kinit_admin(master)
|
||||
+ master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '0'])
|
||||
+ tasks.user_add(master, USER1, password=TMP_PASSWORD)
|
||||
+ # Enforce use of OTP token for this user
|
||||
+ master.run_command(['ipa', 'user-mod', USER1,
|
||||
+ '--user-auth-type=otp'])
|
||||
+ try:
|
||||
+ # Change initial password through the IPA endpoint
|
||||
+ url = f'https://{master.hostname}/ipa/session/change_password'
|
||||
+ master.run_command(['curl', '-d', f'user={USER1}',
|
||||
+ '-d', f'old_password={TMP_PASSWORD}',
|
||||
+ '-d', f'new_password={PASSWORD}',
|
||||
+ '--referer', f'https://{master.hostname}/ipa',
|
||||
+ url])
|
||||
+ conn = master.ldap_connect()
|
||||
+ # First, attempt authenticating with a password but without LDAP
|
||||
+ # control to enforce OTP presence and without server-side
|
||||
+ # enforcement of the OTP presence check.
|
||||
+ conn.simple_bind(binddn, f"{PASSWORD}")
|
||||
+
|
||||
+ # Add an OTP token and then modify it to be expired
|
||||
+ otpuid, totp = add_otptoken(master, USER1, otptype="totp")
|
||||
+
|
||||
+ # Make sure OTP auth is working
|
||||
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
|
||||
+ conn = master.ldap_connect()
|
||||
+ conn.simple_bind(binddn, f"{PASSWORD}{otpvalue}",
|
||||
+ client_controls=controls)
|
||||
+ conn.unbind()
|
||||
+
|
||||
+ # Modfy token so that is now expired
|
||||
+ args = [
|
||||
+ "ipa",
|
||||
+ "otptoken-mod",
|
||||
+ otpuid,
|
||||
+ "--not-after",
|
||||
+ "20241001010000Z",
|
||||
+ ]
|
||||
+ master.run_command(args)
|
||||
+
|
||||
+ # Next, authenticate with Password+OTP again and with the LDAP
|
||||
+ # control this operation should now fail
|
||||
+ time.sleep(45)
|
||||
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
|
||||
+
|
||||
+ conn = master.ldap_connect()
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ conn.simple_bind(binddn, f"{PASSWORD}{otpvalue}",
|
||||
+ client_controls=controls)
|
||||
+
|
||||
+ # Sleep to make sure we are going to use a different token value
|
||||
+ time.sleep(45)
|
||||
+
|
||||
+ # Use OTP token again but authenticate over ssh and make sure it
|
||||
+ # doesn't fallthrough to asking for a password
|
||||
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
|
||||
+ answers = {
|
||||
+ 'Enter first factor:': PASSWORD,
|
||||
+ 'Enter second factor:': otpvalue
|
||||
+ }
|
||||
+ with pytest.raises(AuthenticationException):
|
||||
+ # ssh should fail and NOT ask for a password
|
||||
+ ssh_2f(master.hostname, USER1, answers,
|
||||
+ unwanted_prompt="Password:")
|
||||
+
|
||||
+ # Remove token
|
||||
+ del_otptoken(self.master, otpuid)
|
||||
+
|
||||
+ finally:
|
||||
+ master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '1'])
|
||||
+ master.run_command(['ipa', 'user-del', USER1])
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -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
SOURCES/0016-Handle-IPACertificate-types-in-xmlrpc.patch
Normal file
43
SOURCES/0016-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
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
From 761647f842567713032709753b6d63467d9871a6 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Mon, 23 Sep 2024 14:05:43 +0530
|
||||
Subject: [PATCH] ipatests: Activate ssh in sssd.conf
|
||||
|
||||
This testcase checks that services: ssh
|
||||
is included in the sssd.conf file when
|
||||
ipa-client-install is successful.
|
||||
|
||||
Ref: https://pagure.io/freeipa/issue/9649
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_installation_client.py | 10 ++++++++++
|
||||
1 file changed, 10 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_installation_client.py b/ipatests/test_integration/test_installation_client.py
|
||||
index f8567b39eead4dffd522aad504fa72a086969257..884bff2f2f3e49f66da391424e128d8e31b82a8a 100644
|
||||
--- a/ipatests/test_integration/test_installation_client.py
|
||||
+++ b/ipatests/test_integration/test_installation_client.py
|
||||
@@ -94,6 +94,16 @@ class TestInstallClient(IntegrationTest):
|
||||
).encode() not in krb5_cfg
|
||||
tasks.uninstall_client(self.clients[0])
|
||||
|
||||
+ def test_check_ssh_service_is_activated(self):
|
||||
+ """
|
||||
+ This test checks all default services are activated
|
||||
+ in sssd.conf including ssh
|
||||
+ """
|
||||
+ tasks.install_client(self.master, self.clients[0])
|
||||
+ sssd_cfg = self.clients[0].get_file_contents(paths.SSSD_CONF)
|
||||
+ assert 'services = nss, pam, ssh, sudo' in sssd_cfg.decode()
|
||||
+ tasks.uninstall_client(self.clients[0])
|
||||
+
|
||||
def test_install_with_automount(self):
|
||||
"""Test that installation with automount is successful"""
|
||||
tasks.install_client(self.master, self.clients[0],
|
||||
--
|
||||
2.46.2
|
||||
|
||||
159
SOURCES/0017-Replace-None-with-when-uninstalling-CA.patch
Normal file
159
SOURCES/0017-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
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
From f978fa05e3ed9d4ad9d20493c05c77fb9b4976a7 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 15 Oct 2024 17:04:55 +0200
|
||||
Subject: [PATCH] ipa-migrate man page: fix typos and errors
|
||||
|
||||
ipa-migrate man page mentions non-existing option --hostname.
|
||||
Fix the SYNOPSIS and various typos.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9681
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Mark Reynolds <mreynolds@redhat.com>
|
||||
---
|
||||
install/tools/man/ipa-migrate.1 | 38 +++++++++++++++------------------
|
||||
1 file changed, 17 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/install/tools/man/ipa-migrate.1 b/install/tools/man/ipa-migrate.1
|
||||
index 47ae47ea4afa3a5a6fe25dd9bbd14c27ab5f1fdb..5106f4f0f5b5928909ccd5abcef3bb6d1586f5df 100644
|
||||
--- a/install/tools/man/ipa-migrate.1
|
||||
+++ b/install/tools/man/ipa-migrate.1
|
||||
@@ -5,11 +5,11 @@
|
||||
.SH "NAME"
|
||||
ipa\-migrate \- Migrate an IPA server from one machine to another
|
||||
.SH "SYNOPSIS"
|
||||
-ipa\-migrate
|
||||
+\fBipa\-migrate\fR [OPTIONS] \fBprod\-mode\fR|\fBstage\-mode\fR \fIhostname\fR
|
||||
.SH "DESCRIPTION"
|
||||
|
||||
Use the \fIipa-migrate\fR command to migrate one
|
||||
-IPA server to an existing local IPA server installation.
|
||||
+IPA server \fIhostname\fR to an existing local IPA server installation.
|
||||
|
||||
Migrate IPA schema, configuration, and database to a local IPA server. This
|
||||
migration can be done online, where the tool will query the remote server. Or,
|
||||
@@ -19,7 +19,6 @@ and then use an exported LDIF file for the database migration portion (this
|
||||
might be more useful for very large databases as you don't need to worry about
|
||||
network interruptions)
|
||||
|
||||
-.SH POSITIONAL ARGUMENTS
|
||||
.TP
|
||||
\fBprod\-mode\fR
|
||||
In this mode everything will be migrated including the current user SIDs and
|
||||
@@ -28,13 +27,10 @@ DNA ranges
|
||||
\fBstage\-mode\fR
|
||||
In this mode, SIDs & DNA ranges are not migrated, and DNA attributes are reset
|
||||
|
||||
-.SH "COMMANDS"
|
||||
+.SH "OPTIONS"
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
-Use verbose output while running the migration tool.
|
||||
-.TP
|
||||
-\fB\-e\fR, \fB\-\-hostname=HOSTNAME\fR
|
||||
-The host name of the remote IPA server that is being migrated from.
|
||||
+Use verbose output while running the migration tool
|
||||
.TP
|
||||
\fB\-D\fR, \fB\-\-bind\-dn=BIND_DN\fR
|
||||
The Bind DN (Distinguished Name) or an LDAP entry to bind to the remote IPA server with.
|
||||
@@ -43,10 +39,10 @@ access to read the userPassword attribute. If ommitted the default is "cn=direc
|
||||
.TP
|
||||
\fB\-w\fR, \fB\-\-bind\-pw=PASSWORD\fR
|
||||
The password for the Bind DN that is authenticating against the remote IPA server. If
|
||||
-a password is not provided then the tool with prompt for the password if needed.
|
||||
+a password is not provided then the tool with prompt for the password if needed
|
||||
.TP
|
||||
-\fB\-Just\fR, \fB\-\-bind\-pw\-file=FILE_PATH\fR
|
||||
-Path to a file containing the password for the Bind DN.
|
||||
+\fB\-j\fR, \fB\-\-bind\-pw\-file=FILE_PATH\fR
|
||||
+Path to a file containing the password for the Bind DN
|
||||
.TP
|
||||
\fB\-Z\fR, \fB\-\-cacertfile=FILE_PATH\fR
|
||||
Path to a file containing a CA Certificate that the remote server trusts
|
||||
@@ -55,23 +51,23 @@ Path to a file containing a CA Certificate that the remote server trusts
|
||||
Path to a file containing the migration log. By default the tool will use \fI/var/log/ipa-migrate.log\fR
|
||||
.TP
|
||||
\fB\-x\fR, \fB\-\-dryrun\fR
|
||||
-Go through the migration process but do not write and data to the new IPA server.
|
||||
+Go through the migration process but do not write any data to the new IPA server
|
||||
.TP
|
||||
\fB\-o\fR, \fB\-\-dryrun\-record=FILE_PATH\fR
|
||||
Go through the migration process but do not write any data to the new IPA server. However, write the
|
||||
-migration operations to an LDIF file which can be applied later or reused for multiple migrations.
|
||||
+migration operations to an LDIF file which can be applied later or reused for multiple migrations
|
||||
.TP
|
||||
\fB\-r\fR, \fB\-\-reset\-range\fR
|
||||
Reset the ID range for migrated users/groups. In "stage-mode" this is done automatically
|
||||
.TP
|
||||
\fB\-F\fR, \fB\-\-force\fR
|
||||
-Ignore any errors and continue to proceed with migration effort.
|
||||
+Ignore any errors and continue to proceed with migration effort
|
||||
.TP
|
||||
\fB\-q\fR, \fB\-\-quiet\fR
|
||||
-Only log errors during the migration process.
|
||||
+Only log errors during the migration process
|
||||
.TP
|
||||
\fB\-B\fR, \fB\-\-migrate\-dns\fR
|
||||
-Migrate thr DNS records
|
||||
+Migrate the DNS records
|
||||
.TP
|
||||
\fB\-S\fR, \fB\-\-skip\-schema\fR
|
||||
Do not migrate the database schema
|
||||
@@ -80,21 +76,21 @@ Do not migrate the database schema
|
||||
Do not migrate the database configuration (dse.ldif/cn=config)
|
||||
.TP
|
||||
\fB\-O\fR, \fB\-\-schema\-overwrite\fR
|
||||
-Overwrite existing schema definitions. By default duplicate schema is skipped.
|
||||
+Overwrite existing schema definitions. By default duplicate schema is skipped
|
||||
.TP
|
||||
\fB\-s\fR, \fB\-\-subtree=DN\fR
|
||||
Specifies a custom database subtree that should be included in the migration.
|
||||
This is only needed if non-default subtrees/branches were added to the database
|
||||
-outside of IPA.
|
||||
+outside of IPA
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-db\-ldif=FILE_PATH\fR
|
||||
-LDIF file containing the entire backend. If omitted the tool will query the remote IPA server.
|
||||
+LDIF file containing the entire backend. If omitted the tool will query the remote IPA server
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-schema\-ldif=FILE_PATH\fR
|
||||
-LDIF file containing the schema. If omitted the tool will query the remote IPA server.
|
||||
+LDIF file containing the schema. If omitted the tool will query the remote IPA server
|
||||
.TP
|
||||
\fB\-g\fR, \fB\-\-config\-ldif=FILE_PATH\fR
|
||||
-LDIF file containing the entire "cn=config" DIT. If omitted the tool will query the remote IPA server.
|
||||
+LDIF file containing the entire "cn=config" DIT. If omitted the tool will query the remote IPA server
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-no\-prompt\fR
|
||||
Do not prompt for confirmation before starting migration. Use at your own risk!
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
From 7f4e7e1d6a2ae9d05a2dfcf620f4df07d09d9d2b Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Thu, 3 Oct 2024 18:45:31 +0530
|
||||
Subject: [PATCH] ipatests: Test for ipa hbac rule duplication
|
||||
|
||||
This test checks that ipa-migrate is not creating duplicate default hbac rules
|
||||
for allow_all and allow_systemd-user rules.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9640
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
---
|
||||
.../test_ipa_ipa_migration.py | 26 +++++++++++++++++++
|
||||
1 file changed, 26 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
index 288165e8a83a96e6f6bd4e52866f98617f497c56..70c268951a0d7e40806742b16e62b764b2bae37b 100644
|
||||
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
@@ -9,6 +9,7 @@ from __future__ import absolute_import
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
from ipaplatform.paths import paths
|
||||
+from collections import Counter
|
||||
|
||||
import pytest
|
||||
import textwrap
|
||||
@@ -920,3 +921,28 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
)
|
||||
assert result.returncode == 1
|
||||
assert ERR_MSG in result.stderr_text
|
||||
+
|
||||
+ def test_ipa_hbac_rule_duplication(self):
|
||||
+ """
|
||||
+ This testcase checks that default hbac rules
|
||||
+ are not duplicated on the local server when
|
||||
+ ipa-migrate command is run.
|
||||
+ """
|
||||
+ run_migrate(
|
||||
+ self.replicas[0],
|
||||
+ "prod-mode",
|
||||
+ self.master.hostname,
|
||||
+ "cn=Directory Manager",
|
||||
+ self.master.config.admin_password,
|
||||
+ extra_args=['-n']
|
||||
+ )
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ['ipa', 'hbacrule-find']
|
||||
+ )
|
||||
+ lines = result.stdout_text.splitlines()
|
||||
+ line = []
|
||||
+ for i in lines:
|
||||
+ line.append(i.strip())
|
||||
+ count = Counter(line)
|
||||
+ assert count.get('Rule name: allow_all') < 2
|
||||
+ assert count.get('Rule name: allow_systemd-user') < 2
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From 0f691463c379861ca5be88f99d02a313941a39d3 Mon Sep 17 00:00:00 2001
|
||||
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
|
||||
@ -14,6 +14,8 @@ 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(-)
|
||||
@ -1,116 +0,0 @@
|
||||
From 142f52fc981fe9f1d693b79a7b49506af2e98829 Mon Sep 17 00:00:00 2001
|
||||
From: Mohammad Rizwan <myusuf@redhat.com>
|
||||
Date: Mon, 19 Aug 2024 16:08:53 +0530
|
||||
Subject: [PATCH] ipatests: refactor password file handling in TestHSMInstall
|
||||
|
||||
When token and associated certs are not being cleaned
|
||||
up properly, the subsequent installation fails. Hence
|
||||
Password file related scenarios moved out to new test class
|
||||
so that it have fresh installation.
|
||||
|
||||
Signed-off-by: Mohammad Rizwan <myusuf@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
---
|
||||
.../nightly_ipa-4-12_latest.yaml | 12 ++++++++
|
||||
.../nightly_ipa-4-12_latest_selinux.yaml | 13 ++++++++
|
||||
ipatests/test_integration/test_hsm.py | 30 ++++++++++---------
|
||||
3 files changed, 41 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
|
||||
index 6d18e708fb0512ce21d8db68d4f1ab26849f40b7..07e2a8399ae4cc953adb415b975101ed20c67fd2 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
|
||||
@@ -1950,6 +1950,18 @@ jobs:
|
||||
timeout: 6300
|
||||
topology: *master_3repl_1client
|
||||
|
||||
+ fedora-latest-ipa-4-12/test_hsm_TestHSMInstallPasswordFile:
|
||||
+ requires: [fedora-latest-ipa-4-12/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-12/build_url}'
|
||||
+ test_suite: test_integration/test_hsm.py::TestHSMInstallPasswordFile
|
||||
+ template: *ci-ipa-4-12-latest
|
||||
+ timeout: 6300
|
||||
+ topology: *master_1repl
|
||||
+
|
||||
fedora-latest-ipa-4-12/test_hsm_TestHSMInstallADTrustBase:
|
||||
requires: [fedora-latest-ipa-4-12/build]
|
||||
priority: 50
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
|
||||
index 52686df9713975c9590b8a99edb7c3442531fecc..11046be13fca1e7403d0fd74329a66ded3927a6c 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
|
||||
@@ -2105,6 +2105,19 @@ jobs:
|
||||
timeout: 6300
|
||||
topology: *master_3repl_1client
|
||||
|
||||
+ fedora-latest-ipa-4-12/test_hsm_TestHSMInstallPasswordFile:
|
||||
+ requires: [fedora-latest-ipa-4-12/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-12/build_url}'
|
||||
+ selinux_enforcing: True
|
||||
+ test_suite: test_integration/test_hsm.py::TestHSMInstallPasswordFile
|
||||
+ template: *ci-ipa-4-12-latest
|
||||
+ timeout: 6300
|
||||
+ topology: *master_1repl
|
||||
+
|
||||
fedora-latest-ipa-4-12/test_hsm_TestHSMInstallADTrustBase:
|
||||
requires: [fedora-latest-ipa-4-12/build]
|
||||
priority: 50
|
||||
diff --git a/ipatests/test_integration/test_hsm.py b/ipatests/test_integration/test_hsm.py
|
||||
index 374f5c25fd3453cd45a15d2b0f20cee424282595..42895fcd60a7c02d3b6103c2f6751a367da30b2f 100644
|
||||
--- a/ipatests/test_integration/test_hsm.py
|
||||
+++ b/ipatests/test_integration/test_hsm.py
|
||||
@@ -312,24 +312,26 @@ class TestHSMInstall(BaseHSMTest):
|
||||
assert returncode == 0
|
||||
assert output == "No issues found."
|
||||
|
||||
- def test_hsm_install_server_password_file(self):
|
||||
- check_version(self.master)
|
||||
- # cleanup before fresh install with password file
|
||||
- for client in self.clients:
|
||||
- tasks.uninstall_client(client)
|
||||
|
||||
- for replica in self.replicas:
|
||||
- tasks.uninstall_master(replica)
|
||||
+class TestHSMInstallPasswordFile(BaseHSMTest):
|
||||
|
||||
- tasks.uninstall_master(self.master)
|
||||
+ num_replicas = 1
|
||||
|
||||
- delete_hsm_token([self.master] + self.replicas, self.token_name)
|
||||
- self.token_name, self.token_password = get_hsm_token(self.master)
|
||||
- self.master.put_file_contents(self.token_password_file,
|
||||
- self.token_password)
|
||||
- self.replicas[0].put_file_contents(self.token_password_file,
|
||||
- self.token_password)
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ check_version(cls.master)
|
||||
+ # Enable pkiuser to read softhsm tokens
|
||||
+ cls.master.run_command(['usermod', 'pkiuser', '-a', '-G', 'ods'])
|
||||
+ cls.token_name, cls.token_password = get_hsm_token(cls.master)
|
||||
+ cls.master.put_file_contents(
|
||||
+ cls.token_password_file, cls.token_password
|
||||
+ )
|
||||
+ cls.replicas[0].put_file_contents(
|
||||
+ cls.token_password_file, cls.token_password
|
||||
+ )
|
||||
|
||||
+ def test_hsm_install_server_password_file(self):
|
||||
+ check_version(self.master)
|
||||
tasks.install_master(
|
||||
self.master, setup_dns=self.master_with_dns,
|
||||
setup_kra=self.master_with_kra,
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -1,276 +0,0 @@
|
||||
From 6ac11ae003740faf19f3c75bf542ec44f717114f Mon Sep 17 00:00:00 2001
|
||||
From: Madhuri Upadhye <mupadhye@redhat.com>
|
||||
Date: Tue, 23 Jul 2024 18:14:36 +0530
|
||||
Subject: [PATCH] ipatests: 2FA test cases
|
||||
|
||||
Added following:
|
||||
|
||||
Added 'ssh_2fa_with_cmd' method for authentication,
|
||||
as for '\n' with paramiko did not work. In a test case
|
||||
need to just press `Enter` for `second factor`.
|
||||
Advantage of above function is no having paramiko
|
||||
dependancy.
|
||||
We can run the any command in same session after
|
||||
authentication of user.
|
||||
|
||||
Test cases:
|
||||
1. Authenticate the user only with password,
|
||||
just press enter at `Second factor` and check tgt after auth.
|
||||
when User authentication types: otp, password
|
||||
2. Authenticate the user with password and otpvalues and
|
||||
check tgt of user after auth when
|
||||
User authentication types: otp, password
|
||||
|
||||
related: https://github.com/SSSD/sssd/pull/7500
|
||||
|
||||
Signed-off-by: Madhuri Upadhye <mupadhye@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_otp.py | 192 ++++++++++++++++++++++++--
|
||||
1 file changed, 181 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py
|
||||
index 878b4fb560ba8d7768ead54b065656462545babd..0babb45897c6107bf354477dbb0d3a805a3116f5 100644
|
||||
--- a/ipatests/test_integration/test_otp.py
|
||||
+++ b/ipatests/test_integration/test_otp.py
|
||||
@@ -5,26 +5,27 @@
|
||||
"""
|
||||
import base64
|
||||
import logging
|
||||
-import pytest
|
||||
import re
|
||||
-import time
|
||||
+import tempfile
|
||||
import textwrap
|
||||
-from urllib.parse import urlparse, parse_qs
|
||||
-from paramiko import AuthenticationException
|
||||
+import time
|
||||
+from urllib.parse import parse_qs, urlparse
|
||||
|
||||
+import pytest
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.twofactor.hotp import HOTP
|
||||
from cryptography.hazmat.primitives.twofactor.totp import TOTP
|
||||
-
|
||||
-from ipatests.test_integration.base import IntegrationTest
|
||||
-from ipaplatform.paths import paths
|
||||
-from ipatests.pytest_ipa.integration import tasks
|
||||
-from ipapython.dn import DN
|
||||
-
|
||||
from ldap.controls.simple import BooleanControl
|
||||
+from paramiko import AuthenticationException
|
||||
|
||||
from ipalib import errors
|
||||
+from ipaplatform.osinfo import osinfo
|
||||
+from ipaplatform.paths import paths
|
||||
+from ipapython.dn import DN
|
||||
+from ipatests.pytest_ipa.integration import tasks
|
||||
+from ipatests.test_integration.base import IntegrationTest
|
||||
+from ipatests.util import xfail_context
|
||||
|
||||
PASSWORD = "DummyPassword123"
|
||||
USER = "opttestuser"
|
||||
@@ -84,6 +85,65 @@ def kinit_otp(host, user, *, password, otp, success=True):
|
||||
)
|
||||
|
||||
|
||||
+def ssh_2fa_with_cmd(host, hostname, username, password, otpvalue,
|
||||
+ command="exit 0"):
|
||||
+ """ ssh to user and in same session pass the command to check tgt of user
|
||||
+ :param host: host to ssh
|
||||
+ :param hostname: hostname to ssh
|
||||
+ :param str username: The name of user
|
||||
+ :param str password: password, usually the first factor
|
||||
+ :param str otpvalue: generated pin of user
|
||||
+ :param str command: command to execute in same session,
|
||||
+ by deafult set to "exit 0"
|
||||
+ :return: object class of expect command run
|
||||
+ """
|
||||
+ temp_conf = tempfile.NamedTemporaryFile(suffix='.exp', delete=False)
|
||||
+ with open(temp_conf.name, 'w') as tfile:
|
||||
+ tfile.write('proc exitmsg { msg code } {\n')
|
||||
+ tfile.write('\t# Close spawned program, if we are in the prompt\n')
|
||||
+ tfile.write('\tcatch close\n\n')
|
||||
+ tfile.write('\t# Wait for the exit code\n')
|
||||
+ tfile.write('\tlassign [wait] pid spawnid os_error_flag rc\n\n')
|
||||
+ tfile.write('\tputs ""\n')
|
||||
+ tfile.write('\tputs "expect result: $msg"\n')
|
||||
+ tfile.write('\tputs "expect exit code: $code"\n')
|
||||
+ tfile.write('\tputs "expect spawn exit code: $rc"\n')
|
||||
+ tfile.write('\texit $code\n')
|
||||
+ tfile.write('}\n')
|
||||
+ tfile.write('set timeout 60\n')
|
||||
+ tfile.write('set prompt ".*\\[#\\$>\\] $"\n')
|
||||
+ tfile.write(f'set password "{password}"\n')
|
||||
+ tfile.write(f'set otpvalue "{otpvalue}"\n')
|
||||
+ tfile.write(f'spawn ssh -o NumberOfPasswordPrompts=1 -o '
|
||||
+ f'StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
|
||||
+ f' -l {username} {hostname} {command}\n')
|
||||
+ tfile.write('expect {\n')
|
||||
+ tfile.write('"Enter first factor:*" {send -- "$password\r"}\n')
|
||||
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
|
||||
+ tfile.write('eof {exitmsg "Unexpected end of file" 202}\n')
|
||||
+ tfile.write('}\n')
|
||||
+ tfile.write('expect {\n')
|
||||
+ tfile.write('"Enter second factor:*" {send -- "$otpvalue\r"}\n')
|
||||
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
|
||||
+ tfile.write('eof {exitmsg "Unexpected end of file" 202}\n')
|
||||
+ tfile.write('}\n')
|
||||
+ tfile.write('expect {\n')
|
||||
+ tfile.write('"Authentication failure" '
|
||||
+ '{exitmsg "Authentication failure" 1}\n')
|
||||
+ tfile.write('eof {exitmsg "Authentication successful" 0}\n')
|
||||
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
|
||||
+ tfile.write('}\n')
|
||||
+ tfile.write('expect {\n')
|
||||
+ tfile.write('exitmsg "Unexpected code path" 203\n')
|
||||
+ tfile.write('EOF\n')
|
||||
+ tfile.write('}')
|
||||
+ host.transport.put_file(temp_conf.name, '/tmp/ssh.exp')
|
||||
+ tasks.clear_sssd_cache(host)
|
||||
+ expect_cmd = 'expect -f /tmp/ssh.exp'
|
||||
+ cmd = host.run_command(expect_cmd, raiseonerr=False)
|
||||
+ return cmd
|
||||
+
|
||||
+
|
||||
def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
|
||||
"""
|
||||
:param hostname: hostname
|
||||
@@ -91,6 +151,7 @@ def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
|
||||
:param answers_dict: dictionary of options with prompt_message and value.
|
||||
:param port: port for ssh
|
||||
"""
|
||||
+
|
||||
# Handler for server questions
|
||||
def answer_handler(title, instructions, prompt_list):
|
||||
resp = []
|
||||
@@ -131,8 +192,9 @@ class TestOTPToken(IntegrationTest):
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
- super(TestOTPToken, cls).install(mh)
|
||||
master = cls.master
|
||||
+ tasks.install_packages(master, ['expect'])
|
||||
+ super(TestOTPToken, cls).install(mh)
|
||||
|
||||
tasks.kinit_admin(master)
|
||||
# create service with OTP auth indicator
|
||||
@@ -398,6 +460,114 @@ class TestOTPToken(IntegrationTest):
|
||||
self.master.run_command(['semanage', 'login', '-D'])
|
||||
sssd_conf_backup.restore()
|
||||
|
||||
+ def test_2fa_only_with_password(self):
|
||||
+ """Test ssh with 2FA only with the password(first factor) when
|
||||
+ user-auth-type is opt and password.
|
||||
+
|
||||
+ Test for : https://github.com/SSSD/sssd/pull/7500
|
||||
+
|
||||
+ Add the IPA user and user-auth-type set to opt and password.
|
||||
+ Authenticate the user only with password, just press enter
|
||||
+ at `Second factor`
|
||||
+ """
|
||||
+ master = self.master
|
||||
+ USER3 = 'sshuser3'
|
||||
+ sssd_conf_backup = tasks.FileBackup(master, paths.SSSD_CONF)
|
||||
+ first_prompt = 'Enter first factor:'
|
||||
+ second_prompt = 'Enter second factor:'
|
||||
+ add_contents = textwrap.dedent('''
|
||||
+ [prompting/2fa/sshd]
|
||||
+ single_prompt = False
|
||||
+ first_prompt = {0}
|
||||
+ second_prompt = {1}
|
||||
+ ''').format(first_prompt, second_prompt)
|
||||
+ set_sssd_conf(master, add_contents)
|
||||
+ tasks.create_active_user(master, USER3, PASSWORD)
|
||||
+ tasks.kinit_admin(master)
|
||||
+ master.run_command(['ipa', 'user-mod', USER3, '--user-auth-type=otp',
|
||||
+ '--user-auth-type=password'])
|
||||
+ try:
|
||||
+ otpuid, totp = add_otptoken(master, USER3, otptype='totp')
|
||||
+ master.run_command(['ipa', 'otptoken-show', otpuid])
|
||||
+ totp.generate(int(time.time())).decode('ascii')
|
||||
+ otpvalue = "\n"
|
||||
+ tasks.clear_sssd_cache(self.master)
|
||||
+ github_ticket = "https://github.com/SSSD/sssd/pull/7500"
|
||||
+ sssd_version = tasks.get_sssd_version(master)
|
||||
+ rhel_fail = (
|
||||
+ osinfo.id == 'rhel'
|
||||
+ and sssd_version < tasks.parse_version("2.9.5")
|
||||
+ )
|
||||
+ fedora_fail = (
|
||||
+ osinfo.id == 'fedora'
|
||||
+ and sssd_version == tasks.parse_version("2.9.5")
|
||||
+ )
|
||||
+ with xfail_context(rhel_fail or fedora_fail, reason=github_ticket):
|
||||
+ result = ssh_2fa_with_cmd(master,
|
||||
+ self.master.external_hostname,
|
||||
+ USER3, PASSWORD, otpvalue=otpvalue,
|
||||
+ command="klist")
|
||||
+ print(result.stdout_text)
|
||||
+ assert ('Authentication successful') in result.stdout_text
|
||||
+ assert USER3 in result.stdout_text
|
||||
+ assert (f'Default principal: '
|
||||
+ f'{USER3}@{self.master.domain.realm}' in
|
||||
+ result.stdout_text)
|
||||
+ cmd = self.master.run_command(['semanage', 'login', '-l'])
|
||||
+ assert USER3 in cmd.stdout_text
|
||||
+ finally:
|
||||
+ master.run_command(['ipa', 'user-del', USER3])
|
||||
+ self.master.run_command(['semanage', 'login', '-D'])
|
||||
+ sssd_conf_backup.restore()
|
||||
+
|
||||
+ def test_2fa_with_otp_password(self):
|
||||
+ """Test ssh with 2FA only with password and otpvalue when
|
||||
+ user-auth-type is opt and password.
|
||||
+
|
||||
+ Test for : https://github.com/SSSD/sssd/pull/7500
|
||||
+
|
||||
+ Add the IPA user and user-auth-type set to opt and password.
|
||||
+ Authenticate the user only with password and otpvalue.
|
||||
+ """
|
||||
+ master = self.master
|
||||
+ USER4 = 'sshuser4'
|
||||
+ sssd_conf_backup = tasks.FileBackup(master, paths.SSSD_CONF)
|
||||
+ first_prompt = 'Enter first factor:'
|
||||
+ second_prompt = 'Enter second factor:'
|
||||
+ add_contents = textwrap.dedent('''
|
||||
+ [prompting/2fa/sshd]
|
||||
+ single_prompt = False
|
||||
+ first_prompt = {0}
|
||||
+ second_prompt = {1}
|
||||
+ ''').format(first_prompt, second_prompt)
|
||||
+ set_sssd_conf(master, add_contents)
|
||||
+ tasks.create_active_user(master, USER4, PASSWORD)
|
||||
+ tasks.kinit_admin(master)
|
||||
+
|
||||
+ master.run_command(['ipa', 'user-mod', USER4, '--user-auth-type=otp',
|
||||
+ '--user-auth-type=password'])
|
||||
+ try:
|
||||
+ otpuid, totp = add_otptoken(master, USER4, otptype='totp')
|
||||
+ master.run_command(['ipa', 'otptoken-show', otpuid])
|
||||
+ otpvalue = totp.generate(int(time.time())).decode('ascii')
|
||||
+ tasks.clear_sssd_cache(self.master)
|
||||
+ result = ssh_2fa_with_cmd(master,
|
||||
+ self.master.external_hostname,
|
||||
+ USER4, PASSWORD, otpvalue=otpvalue,
|
||||
+ command="klist")
|
||||
+ print(result.stdout_text)
|
||||
+ cmd = self.master.run_command(['semanage', 'login', '-l'])
|
||||
+ # check the output
|
||||
+ assert ('Authentication successful') in result.stdout_text
|
||||
+ assert USER4 in result.stdout_text
|
||||
+ assert (f'Default principal: {USER4}@'
|
||||
+ f'{self.master.domain.realm}' in result.stdout_text)
|
||||
+ assert USER4 in cmd.stdout_text
|
||||
+ finally:
|
||||
+ master.run_command(['ipa', 'user-del', USER4])
|
||||
+ self.master.run_command(['semanage', 'login', '-D'])
|
||||
+ sssd_conf_backup.restore()
|
||||
+
|
||||
@pytest.fixture
|
||||
def setup_otp_nsslapd(self):
|
||||
check_services = self.master.run_command(
|
||||
--
|
||||
2.46.2
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
From d5efb4decb74b50c05b1d252add1c075e660d154 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Wed, 11 Feb 2026 10:23:36 +0100
|
||||
Subject: [PATCH] ipatests: pruning is enabled when RSN is enabled
|
||||
|
||||
The test TestACMEPrune installs the server with --random-serial-numbers
|
||||
but expects pruning to be disabled if the 389ds backend is BDB.
|
||||
|
||||
This is a wrong expectation as pruning is enabled as soon as RSN
|
||||
are enabled (since commit 3777d2b).
|
||||
Fix the test expectation.
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_acme.py | 8 ++------
|
||||
1 file changed, 2 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||
index 4c66e4348beeaca95577a786a46e53fdc1532ef7..bedec9d1f9f0c168c11aceb155978b6a0dae8dd7 100644
|
||||
--- a/ipatests/test_integration/test_acme.py
|
||||
+++ b/ipatests/test_integration/test_acme.py
|
||||
@@ -718,12 +718,8 @@ class TestACMEPrune(IntegrationTest):
|
||||
< tasks.parse_version('11.3.0')):
|
||||
raise pytest.skip("Certificate pruning is not available")
|
||||
|
||||
- # Pruning is enabled by default when the host supports lmdb
|
||||
- if get_389ds_backend(self.master) == 'bdb':
|
||||
- cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||
- assert "jobsScheduler.job.pruning.enabled=false".encode() in cs_cfg
|
||||
- self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||||
-
|
||||
+ # Pruning is enabled by default when server is installed
|
||||
+ # with --random-serial-numbers
|
||||
cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||
assert "jobsScheduler.enabled=true".encode() in cs_cfg
|
||||
assert "jobsScheduler.job.pruning.enabled=true".encode() in cs_cfg
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
From 9a2de23eb5e00efa72189c4a86d9db1fab52c2ca Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Thu, 24 Oct 2024 11:49:17 -0400
|
||||
Subject: [PATCH] Small fixup to determine which ACME uninstaller to use
|
||||
|
||||
The conditional was <= 11.5.0 which it should have been
|
||||
< 11.6.0 to allow for small updates to the 11.5.0 branch.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9673
|
||||
Fixes: https://pagure.io/freeipa/issue/9674
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipaserver/install/dogtaginstance.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
|
||||
index 4b0f4d274b0c33140ed6f939f1a3fd8b75930ff9..58421a1d8859e5dd1357e07fd605e84e49048951 100644
|
||||
--- a/ipaserver/install/dogtaginstance.py
|
||||
+++ b/ipaserver/install/dogtaginstance.py
|
||||
@@ -311,7 +311,7 @@ class DogtagInstance(service.Service):
|
||||
return
|
||||
elif (
|
||||
pki.util.Version("11.0.0") <= pki_version
|
||||
- <= pki.util.Version("11.5.0")
|
||||
+ < pki.util.Version("11.6.0")
|
||||
):
|
||||
args = ['pki-server', 'acme-remove']
|
||||
else:
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From 8419410a181c411e37d5ef513691b72417423285 Mon Sep 17 00:00:00 2001
|
||||
From ee33a646ac9ad4c2d028e7633c2091239a3c0d6b Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Wed, 21 Jan 2026 13:48:05 +0530
|
||||
Subject: [PATCH] ipatests: Add DNS bugzilla integration tests.
|
||||
@ -14,8 +14,8 @@ Added TestDNSBugs class with tests for:
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9911
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipatests/pytest_ipa/integration/tasks.py | 65 +-
|
||||
ipatests/test_integration/test_dns.py | 1142 +++++++++++++++++++++-
|
||||
@ -23,7 +23,7 @@ Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
3 files changed, 1289 insertions(+), 60 deletions(-)
|
||||
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index 47330d6d93401485e4eb7b2501cf5ea37498d719..d9f142794521ced8f96c2af46b1d2fb59cdcad87 100755
|
||||
index 47330d6d9..d9f142794 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -1629,7 +1629,7 @@ def add_a_record(master, host):
|
||||
@ -133,7 +133,7 @@ index 47330d6d93401485e4eb7b2501cf5ea37498d719..d9f142794521ced8f96c2af46b1d2fb5
|
||||
|
||||
|
||||
diff --git a/ipatests/test_integration/test_dns.py b/ipatests/test_integration/test_dns.py
|
||||
index 905227497c8879bfe4cf8b027e396689c0451208..6840a10d1a301a8f19cfdf95896e08be7cfd7ea2 100644
|
||||
index 905227497..6840a10d1 100644
|
||||
--- a/ipatests/test_integration/test_dns.py
|
||||
+++ b/ipatests/test_integration/test_dns.py
|
||||
@@ -7,7 +7,9 @@ from __future__ import absolute_import
|
||||
@ -1442,7 +1442,7 @@ index 905227497c8879bfe4cf8b027e396689c0451208..6840a10d1a301a8f19cfdf95896e08be
|
||||
+ '/sbin/ip', '-6', 'addr', 'del', temp_ipv6, 'dev', eth
|
||||
+ ], raiseonerr=False)
|
||||
diff --git a/ipatests/test_xmlrpc/test_dns_plugin.py b/ipatests/test_xmlrpc/test_dns_plugin.py
|
||||
index bff4b40aef6e5adec21c8929719e99669b80cdf0..3ab54674b8a0154f5a4ef36568ba53af73548f79 100644
|
||||
index bff4b40ae..3ab54674b 100644
|
||||
--- a/ipatests/test_xmlrpc/test_dns_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_dns_plugin.py
|
||||
@@ -529,6 +529,16 @@ class test_dns(Declarative):
|
||||
154
SOURCES/0022-Avoid-int-overflow-with-pwpolicy-minlife.patch
Normal file
154
SOURCES/0022-Avoid-int-overflow-with-pwpolicy-minlife.patch
Normal file
@ -0,0 +1,154 @@
|
||||
From 7b0ac4f3dd8febf0c22a11bf2db9b780a242e302 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Mon, 26 Jan 2026 19:25:17 -0500
|
||||
Subject: [PATCH] Avoid int overflow with pwpolicy minlife
|
||||
|
||||
This converts the value to an unsigned integer instead and
|
||||
sets the max value to match the maxlife (~54 years).
|
||||
|
||||
A syslog log message was added to explain why the "too young"
|
||||
message is returned
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9929
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
---
|
||||
.../ipa-slapi-plugins/ipa-pwd-extop/common.c | 2 +-
|
||||
ipaserver/plugins/pwpolicy.py | 1 +
|
||||
ipatests/test_integration/test_pwpolicy.py | 50 ++++++++++++++++---
|
||||
util/ipa_pwd.c | 1 +
|
||||
util/ipa_pwd.h | 2 +-
|
||||
5 files changed, 48 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
index 0e69f3410..71642540a 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
@@ -407,7 +407,7 @@ int ipapwd_getPolicy(const char *dn,
|
||||
}
|
||||
|
||||
/* read data out of policy object */
|
||||
- policy->min_pwd_life = slapi_entry_attr_get_int(pe, "krbMinPwdLife");
|
||||
+ policy->min_pwd_life = slapi_entry_attr_get_uint(pe, "krbMinPwdLife");
|
||||
|
||||
policy->max_pwd_life = slapi_entry_attr_get_int(pe, "krbMaxPwdLife");
|
||||
|
||||
diff --git a/ipaserver/plugins/pwpolicy.py b/ipaserver/plugins/pwpolicy.py
|
||||
index e49a2e1cd..2d9907d9d 100644
|
||||
--- a/ipaserver/plugins/pwpolicy.py
|
||||
+++ b/ipaserver/plugins/pwpolicy.py
|
||||
@@ -338,6 +338,7 @@ class pwpolicy(LDAPObject):
|
||||
label=_('Min lifetime (hours)'),
|
||||
doc=_('Minimum password lifetime (in hours)'),
|
||||
minvalue=0,
|
||||
+ maxvalue=480000, # about 54 years, same as maxlife
|
||||
),
|
||||
Int('krbpwdhistorylength?',
|
||||
cli_name='history',
|
||||
diff --git a/ipatests/test_integration/test_pwpolicy.py b/ipatests/test_integration/test_pwpolicy.py
|
||||
index a627b66ce..f7fd40ada 100644
|
||||
--- a/ipatests/test_integration/test_pwpolicy.py
|
||||
+++ b/ipatests/test_integration/test_pwpolicy.py
|
||||
@@ -16,17 +16,14 @@ PASSWORD = 'Secret123'
|
||||
POLICY = 'test'
|
||||
|
||||
|
||||
-class TestPWPolicy(IntegrationTest):
|
||||
+class BasePWpolicy(IntegrationTest):
|
||||
"""
|
||||
- Test password policy in action.
|
||||
+ Base class for testing password policies including libpwquality
|
||||
"""
|
||||
- num_replicas = 1
|
||||
-
|
||||
- topology = 'line'
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
- super(TestPWPolicy, cls).install(mh)
|
||||
+ tasks.install_master(cls.master, setup_dns=True)
|
||||
|
||||
tasks.kinit_admin(cls.master)
|
||||
cls.master.run_command(['ipa', 'user-add', USER,
|
||||
@@ -66,6 +63,15 @@ class TestPWPolicy(IntegrationTest):
|
||||
host.run_command(['ipa', 'user-unlock', user])
|
||||
tasks.kdestroy_all(host)
|
||||
|
||||
+
|
||||
+class TestPWquality(BasePWpolicy):
|
||||
+ """
|
||||
+ libpwquality tests
|
||||
+ """
|
||||
+ num_replicas = 1
|
||||
+
|
||||
+ topology = 'line'
|
||||
+
|
||||
def set_pwpolicy(self, minlength=None, maxrepeat=None, maxsequence=None,
|
||||
dictcheck=None, usercheck=None, minclasses=None,
|
||||
dcredit=None, ucredit=None, lcredit=None, ocredit=None):
|
||||
@@ -534,3 +540,35 @@ class TestPWPolicy(IntegrationTest):
|
||||
self.master, dn, ['passwordgraceusertime',],
|
||||
)
|
||||
assert 'passwordgraceusertime: 0' in result.stdout_text.lower()
|
||||
+
|
||||
+
|
||||
+class TestPWpolicy(BasePWpolicy):
|
||||
+ """
|
||||
+ Tests for original/Kerberos password policies. Excludes libpwquality
|
||||
+ """
|
||||
+
|
||||
+ # NOTE: set/reset/clear methods to be added later once there is more
|
||||
+ # than a single test.
|
||||
+
|
||||
+ def test_minlife_overflow(self):
|
||||
+ """Test that a large minlife doesn't overflow an unsigned int."""
|
||||
+ newpassword = "Secret.1234"
|
||||
+
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+
|
||||
+ self.master.run_command(
|
||||
+ ["ipa", "pwpolicy-mod", POLICY, "--minlife", "480000",
|
||||
+ "--maxlife", "20000",]
|
||||
+ )
|
||||
+
|
||||
+ self.kinit_as_user(self.master, PASSWORD, PASSWORD)
|
||||
+
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa", "passwd", USER],
|
||||
+ raiseonerr=False,
|
||||
+ stdin_text='{password}\n{password}\n{newpassword}'.format(
|
||||
+ password=PASSWORD, newpassword=newpassword
|
||||
+ ))
|
||||
+
|
||||
+ assert result.returncode == 1
|
||||
+ assert "Too soon to change password" in result.stderr_text
|
||||
diff --git a/util/ipa_pwd.c b/util/ipa_pwd.c
|
||||
index ba6860106..56135aaff 100644
|
||||
--- a/util/ipa_pwd.c
|
||||
+++ b/util/ipa_pwd.c
|
||||
@@ -561,6 +561,7 @@ int ipapwd_check_policy(struct ipapwd_policy *policy,
|
||||
* policy is set */
|
||||
|
||||
if (cur_time < last_pwd_change + policy->min_pwd_life) {
|
||||
+ syslog(LOG_ERR, "Password too young. %d seconds since last change, policy requires %u seconds.\n", (cur_time - last_pwd_change), policy->min_pwd_life);
|
||||
return IPAPWD_POLICY_PWD_TOO_YOUNG;
|
||||
}
|
||||
}
|
||||
diff --git a/util/ipa_pwd.h b/util/ipa_pwd.h
|
||||
index aa2c6e978..d3c5f8be7 100644
|
||||
--- a/util/ipa_pwd.h
|
||||
+++ b/util/ipa_pwd.h
|
||||
@@ -54,7 +54,7 @@ enum ipapwd_error {
|
||||
};
|
||||
|
||||
struct ipapwd_policy {
|
||||
- int min_pwd_life;
|
||||
+ unsigned int min_pwd_life;
|
||||
int max_pwd_life;
|
||||
int min_pwd_length;
|
||||
int history_length;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
From a9e653ca36a0829ae59cd204e7388d7a6c91e082 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Fri, 13 Sep 2024 09:58:36 +0200
|
||||
Subject: [PATCH] UnsafeIPAddress: pass flag=0 to IPNetwork
|
||||
|
||||
When parsing a string, the constructor tries to parse the value
|
||||
as an IP Address first, or falls back to an IPNetwork with the
|
||||
flags INET_PTON.
|
||||
|
||||
Use the flag 0 instead for an IPNetwork.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9645
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipapython/ipautil.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
|
||||
index 3e98bfd6a66f24933e7e4de8efb79f4f5bf8bd0e..c237d59fb4b8be4187fb0efb04b097ff4df6c182 100644
|
||||
--- a/ipapython/ipautil.py
|
||||
+++ b/ipapython/ipautil.py
|
||||
@@ -119,7 +119,7 @@ class UnsafeIPAddress(netaddr.IPAddress):
|
||||
if addr.version != 6:
|
||||
raise
|
||||
except ValueError:
|
||||
- self._net = netaddr.IPNetwork(addr, flags=self.netaddr_ip_flags)
|
||||
+ self._net = netaddr.IPNetwork(addr, flags=0)
|
||||
addr = self._net.ip
|
||||
super(UnsafeIPAddress, self).__init__(addr,
|
||||
flags=self.netaddr_ip_flags)
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
From 3d0962014adda39b754c4274ccb5ca5d70963c33 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 21 Oct 2024 13:51:13 -0400
|
||||
Subject: [PATCH] ipa-migrate - dryrun write updates crashes when removing
|
||||
values
|
||||
|
||||
When removing values the mod value list is None and that leads to a
|
||||
crash when trying to iterate it. Instead check that the vals are not
|
||||
None before looping.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9682
|
||||
|
||||
Signed-off-by: MArk Reynolds <mreynolds@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/ipa_migrate.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
|
||||
index 38356aa23ea435e2a616f48356feaea7b50dd1e4..f35629378490d3d45ca97f2aa5b4390c67d660ed 100644
|
||||
--- a/ipaserver/install/ipa_migrate.py
|
||||
+++ b/ipaserver/install/ipa_migrate.py
|
||||
@@ -622,7 +622,7 @@ class IPAMigrate():
|
||||
else:
|
||||
action = "replace"
|
||||
ldif_entry += f"{action}: {attr}\n"
|
||||
- for val in vals:
|
||||
+ for val in list(vals or []):
|
||||
ldif_entry += get_ldif_attr_val(attr, val)
|
||||
ldif_entry += "-\n"
|
||||
ldif_entry += "\n"
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
From 5cd2639f539ce220c291b00afafa72fd35e1d07e Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Mon, 16 Feb 2026 15:47:32 +0100
|
||||
Subject: [PATCH] ipatests: fix install method for BasePWpolicy
|
||||
|
||||
The test was broken by the previous commit, which creates a
|
||||
new test class TestPWquality that inherits from BasePWpolicy.
|
||||
|
||||
As BasePWpolicy overrides the install method with
|
||||
task.install_master instead of super(TestPWPolicy, cls).install(mh),
|
||||
only the master gets installed.
|
||||
|
||||
Fix the BasePWpolicy class.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9946
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_pwpolicy.py | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_pwpolicy.py b/ipatests/test_integration/test_pwpolicy.py
|
||||
index f7fd40ada..a68252bf9 100644
|
||||
--- a/ipatests/test_integration/test_pwpolicy.py
|
||||
+++ b/ipatests/test_integration/test_pwpolicy.py
|
||||
@@ -21,9 +21,12 @@ class BasePWpolicy(IntegrationTest):
|
||||
Base class for testing password policies including libpwquality
|
||||
"""
|
||||
|
||||
+ num_replicas = 0
|
||||
+ topology = 'line'
|
||||
+
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
- tasks.install_master(cls.master, setup_dns=True)
|
||||
+ super(BasePWpolicy, cls).install(mh)
|
||||
|
||||
tasks.kinit_admin(cls.master)
|
||||
cls.master.run_command(['ipa', 'user-add', USER,
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
From 6bdb8603054fc60e9479f6aaf8b6315dfe508891 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Tue, 22 Oct 2024 13:00:03 -0400
|
||||
Subject: [PATCH] ipa-migrate should migrate dns forward zones
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9686
|
||||
|
||||
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/ipa_migrate_constants.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
|
||||
index 250f1b5b01bf066d316a98489ab6153b89615173..c140414ea6c607a93e35ef0705480d1002b7945e 100644
|
||||
--- a/ipaserver/install/ipa_migrate_constants.py
|
||||
+++ b/ipaserver/install/ipa_migrate_constants.py
|
||||
@@ -993,7 +993,7 @@ DB_OBJECTS = {
|
||||
'count': 0,
|
||||
},
|
||||
'dns_records': {
|
||||
- 'oc': ['idnsrecord', 'idnszone'],
|
||||
+ 'oc': ['idnsrecord', 'idnszone', 'idnsforwardzone'],
|
||||
'subtree': ',cn=dns,$SUFFIX',
|
||||
'label': 'DNS Records',
|
||||
'mode': 'all',
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
From 8a7486bc980b7f9b19e6b15dee6642679198c40f Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Mon, 16 Feb 2026 16:18:06 +0100
|
||||
Subject: [PATCH] webui tests: update expected max value for
|
||||
krbminpwdlife
|
||||
|
||||
The Maximum allowed value for krbminpwdlife has been changed
|
||||
by a recent commit to 480000.
|
||||
Update the test to be consistent.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9947
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Carla Martinez <carlmart@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipatests/test_webui/test_pwpolicy.py | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_webui/test_pwpolicy.py b/ipatests/test_webui/test_pwpolicy.py
|
||||
index 74484ab1b..d84bcea41 100644
|
||||
--- a/ipatests/test_webui/test_pwpolicy.py
|
||||
+++ b/ipatests/test_webui/test_pwpolicy.py
|
||||
@@ -151,6 +151,10 @@ class test_pwpolicy(UI_driver):
|
||||
elif field == 'krbpwdmindiffchars':
|
||||
self.check_expected_error(field, 'Maximum value is 5',
|
||||
maximum_value)
|
||||
+ # verifying if field value is more than 480000
|
||||
+ elif field == 'krbminpwdlife':
|
||||
+ self.check_expected_error(field, 'Maximum value is 480000',
|
||||
+ maximum_value)
|
||||
# verifying if field value is more than 2147483647
|
||||
else:
|
||||
self.check_expected_error(field, 'Maximum value is 2147483647',
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From 0430d73f026309b2d2822b3110487a1a2f51bcc7 Mon Sep 17 00:00:00 2001
|
||||
From 69f9978dc8bfe33b0f02cfb62a7d9d6a6051657c Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Thu, 12 Feb 2026 10:54:17 +0530
|
||||
Subject: [PATCH] ipatests: Add DNS integration tests.
|
||||
@ -7,14 +7,15 @@ Tests cover LDAP connection handling, PTR synchronization,
|
||||
update policies, record validation, and zone management fixes.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9911
|
||||
Reviewed-By: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Sudhir Menon <sumenon@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_dns.py | 1078 ++++++++++++++++++++++++-
|
||||
1 file changed, 1076 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_dns.py b/ipatests/test_integration/test_dns.py
|
||||
index 6840a10d1a301a8f19cfdf95896e08be7cfd7ea2..4b9ab1fe8d7b8884760ed637cb2fcc5d5a060df0 100644
|
||||
index 6840a10d1..4b9ab1fe8 100644
|
||||
--- a/ipatests/test_integration/test_dns.py
|
||||
+++ b/ipatests/test_integration/test_dns.py
|
||||
@@ -6,12 +6,11 @@
|
||||
@ -1,784 +0,0 @@
|
||||
From 9da927c8eec7db6d1c75c296eef45beb93797e58 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Thu, 1 Aug 2024 16:30:16 +0530
|
||||
Subject: [PATCH] ipatests: Tests for ipa-migrate tool
|
||||
|
||||
This patch includes test to covers below scenarios
|
||||
|
||||
1. hbac and sudo rules are migrated to local server
|
||||
2. uid for user migrated varies in stage/prod mode.
|
||||
3. subids are migrated to local server
|
||||
4. idranges are migrated to local server
|
||||
5. vaults are not migrated to local server.
|
||||
6. Ensure trust related data is also migrated to local server
|
||||
7. Added paths.IPA_MIGRATE_LOG in ipatests/pytest_ipa/integration/__init__.py
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipatests/pytest_ipa/integration/__init__.py | 2 +
|
||||
.../test_ipa_ipa_migration.py | 596 ++++++++++++++----
|
||||
2 files changed, 460 insertions(+), 138 deletions(-)
|
||||
|
||||
diff --git a/ipatests/pytest_ipa/integration/__init__.py b/ipatests/pytest_ipa/integration/__init__.py
|
||||
index 34b6ef0fb1e49fbb9c86e7496de50cf5cda5e91e..eb032cd72d2aa2a5ed4c476e3cb04dc77f607eaa 100644
|
||||
--- a/ipatests/pytest_ipa/integration/__init__.py
|
||||
+++ b/ipatests/pytest_ipa/integration/__init__.py
|
||||
@@ -88,6 +88,8 @@ CLASS_LOGFILES = [
|
||||
paths.VAR_LOG_AUDIT,
|
||||
# sssd
|
||||
paths.VAR_LOG_SSSD_DIR,
|
||||
+ # ipa-ipa-migration logs
|
||||
+ paths.IPA_MIGRATE_LOG,
|
||||
# system
|
||||
paths.RESOLV_CONF,
|
||||
paths.HOSTS,
|
||||
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
index 70c268951a0d7e40806742b16e62b764b2bae37b..d852ca63a6b3a7e7118d66ce1cd9bb98e56f1a73 100644
|
||||
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
@@ -12,6 +12,7 @@ from ipaplatform.paths import paths
|
||||
from collections import Counter
|
||||
|
||||
import pytest
|
||||
+import re
|
||||
import textwrap
|
||||
|
||||
|
||||
@@ -65,29 +66,7 @@ def prepare_ipa_server(master):
|
||||
"--secondary-rid-base=400000",
|
||||
]
|
||||
)
|
||||
-
|
||||
- # Add Automount locations and maps
|
||||
- master.run_command(["ipa", "automountlocation-add", "baltimore"])
|
||||
- master.run_command(["ipa", "automountmap-add", "baltimore", "auto.share"])
|
||||
- master.run_command(
|
||||
- [
|
||||
- "ipa",
|
||||
- "automountmap-add-indirect",
|
||||
- "baltimore",
|
||||
- "--parentmap=auto.share",
|
||||
- "--mount=sub auto.man",
|
||||
- ]
|
||||
- )
|
||||
- master.run_command(
|
||||
- [
|
||||
- "ipa",
|
||||
- "automountkey-add",
|
||||
- "baltimore",
|
||||
- "auto.master",
|
||||
- "--key=/share",
|
||||
- "--info=auto.share",
|
||||
- ]
|
||||
- )
|
||||
+ master.run_command(["ipactl", "restart"])
|
||||
|
||||
# Run ipa-adtrust-install
|
||||
master.run_command(["dnf", "install", "-y", "ipa-server-trust-ad"])
|
||||
@@ -235,6 +214,17 @@ def prepare_ipa_server(master):
|
||||
["ipa", "hbacrule-add-service", "--hbacsvcs=sshd", "testuser_sshd"]
|
||||
)
|
||||
|
||||
+ # Add DNSForwardzone
|
||||
+ master.run_command(
|
||||
+ [
|
||||
+ "ipa",
|
||||
+ "dnsforwardzone-add",
|
||||
+ "forwardzone.test",
|
||||
+ "--forwarder",
|
||||
+ "10.11.12.13",
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
# Vault addition
|
||||
master.run_command(
|
||||
[
|
||||
@@ -244,6 +234,7 @@ def prepare_ipa_server(master):
|
||||
"vault1234",
|
||||
"--type",
|
||||
"symmetric",
|
||||
+ "testvault",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -260,7 +251,46 @@ def prepare_ipa_server(master):
|
||||
|
||||
# Modify passkeyconfig
|
||||
master.run_command(
|
||||
- ["ipa", "passkeyconfig-mod", "--require-user-verification=FALSE"]
|
||||
+ [
|
||||
+ "ipa", "passkeyconfig-mod",
|
||||
+ "--require-user-verification=FALSE"
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+ # Adding automountlocation, maps, keys
|
||||
+ master.run_command(
|
||||
+ [
|
||||
+ "ipa", "automountlocation-add",
|
||||
+ "baltimore"
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+ master.run_command(
|
||||
+ [
|
||||
+ "ipa", "automountmap-add",
|
||||
+ "baltimore",
|
||||
+ "auto.share"
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+ master.run_command(
|
||||
+ [
|
||||
+ "ipa", "automountmap-add-indirect",
|
||||
+ "baltimore",
|
||||
+ "--parentmap=auto.share",
|
||||
+ "--mount=sub",
|
||||
+ "auto.man",
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+ master.run_command(
|
||||
+ [
|
||||
+ "ipa", "automountkey-add",
|
||||
+ "baltimore",
|
||||
+ "auto.master",
|
||||
+ "--key=/share",
|
||||
+ "--info=auto.share",
|
||||
+ ]
|
||||
)
|
||||
|
||||
|
||||
@@ -288,12 +318,24 @@ def run_migrate(
|
||||
return result
|
||||
|
||||
|
||||
-class TestIPAMigrateScenario1(IntegrationTest):
|
||||
+@pytest.fixture()
|
||||
+def empty_log_file(request):
|
||||
"""
|
||||
- Tier-1 tests for ipa-migrate tool with DNS enabled on
|
||||
- local and remote server
|
||||
+ This fixture empties the log file before ipa-migrate tool
|
||||
+ is run since the log is appended everytime the tool is run.
|
||||
"""
|
||||
+ request.cls.replicas[0].run_command(
|
||||
+ ["truncate", "-s", "0", paths.IPA_MIGRATE_LOG]
|
||||
+ )
|
||||
+ yield
|
||||
|
||||
+
|
||||
+class MigrationTest(IntegrationTest):
|
||||
+ """
|
||||
+ This class will help setup remote IPA server(cls.master)
|
||||
+ and local IPA server(cls.replicas[0]) and it will
|
||||
+ also prepare the remote IPA before migration actually begins.
|
||||
+ """
|
||||
num_replicas = 1
|
||||
num_clients = 1
|
||||
topology = "line"
|
||||
@@ -303,14 +345,14 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
|
||||
prepare_ipa_server(cls.master)
|
||||
tasks.install_client(cls.master, cls.clients[0], nameservers=None)
|
||||
+ tasks.install_master(cls.replicas[0], setup_dns=True, setup_kra=True)
|
||||
|
||||
- def test_remote_server(self):
|
||||
- """
|
||||
- This test installs IPA server instead of replica on
|
||||
- system under test with the same realm and domain name.
|
||||
- """
|
||||
- tasks.install_master(self.replicas[0], setup_dns=True, setup_kra=True)
|
||||
|
||||
+class TestIPAMigrateCLIOptions(MigrationTest):
|
||||
+ """
|
||||
+ Tests to check CLI options for ipa-migrate tool with
|
||||
+ DNS enabled on local and remote server.
|
||||
+ """
|
||||
def test_ipa_migrate_without_kinit_as_admin(self):
|
||||
"""
|
||||
This test checks that ipa-migrate tool displays
|
||||
@@ -417,7 +459,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
"""
|
||||
ldif_file = "/tmp/test.ldif"
|
||||
param = ['-x', '-o', ldif_file]
|
||||
- run_migrate(
|
||||
+ result = run_migrate(
|
||||
self.replicas[0],
|
||||
"stage-mode",
|
||||
self.master.hostname,
|
||||
@@ -426,45 +468,21 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
extra_args=param,
|
||||
)
|
||||
assert self.replicas[0].transport.file_exists("/tmp/test.ldif")
|
||||
+ assert result.returncode == 0
|
||||
|
||||
- @pytest.fixture()
|
||||
- def empty_log_file(self):
|
||||
- """
|
||||
- This fixture empties the log file before ipa-migrate tool
|
||||
- is run since the log is appended everytime the tool is run.
|
||||
- """
|
||||
- self.replicas[0].run_command(
|
||||
- ["truncate", "-s", "0", paths.IPA_MIGRATE_LOG]
|
||||
- )
|
||||
- yield
|
||||
-
|
||||
- def test_ipa_sigden_plugin_fail_error(self, empty_log_file):
|
||||
- """
|
||||
- This testcase checks that sidgen plugin fail error is
|
||||
- not seen during migrate prod-mode
|
||||
- """
|
||||
- SIDGEN_ERR_MSG = "SIDGEN task failed: \n"
|
||||
- run_migrate(
|
||||
- self.replicas[0],
|
||||
- "stage-mode",
|
||||
- self.master.hostname,
|
||||
- "cn=Directory Manager",
|
||||
- self.master.config.admin_password,
|
||||
- extra_args=['-x'],
|
||||
- )
|
||||
- error_msg = self.replicas[0].get_file_contents(
|
||||
- paths.IPA_MIGRATE_LOG, encoding="utf-8"
|
||||
- )
|
||||
- assert SIDGEN_ERR_MSG not in error_msg
|
||||
-
|
||||
- def test_ipa_migrate_stage_mode_dry_run(self, empty_log_file):
|
||||
+ def test_ipa_migrate_stage_mode_dry_run(self):
|
||||
"""
|
||||
Test ipa-migrate stage mode with dry-run option
|
||||
+ This test also checks SIDGEN task failure is
|
||||
+ not seen in ipa migrate log.
|
||||
"""
|
||||
tasks.kinit_admin(self.master)
|
||||
tasks.kinit_admin(self.replicas[0])
|
||||
+ SIDGEN_ERR_MSG = "SIDGEN task failed: \n"
|
||||
IPA_MIGRATE_STAGE_DRY_RUN_LOG = "--dryrun=True\n"
|
||||
- IPA_SERVER_UPRGADE_LOG = "Skipping ipa-server-upgrade in dryrun mode.\n"
|
||||
+ IPA_SERVER_UPRGADE_LOG = (
|
||||
+ "Skipping ipa-server-upgrade in dryrun mode.\n"
|
||||
+ )
|
||||
IPA_SKIP_SIDGEN_LOG = "Skipping SIDGEN task in dryrun mode."
|
||||
result = run_migrate(
|
||||
self.replicas[0],
|
||||
@@ -481,6 +499,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
assert IPA_MIGRATE_STAGE_DRY_RUN_LOG in install_msg
|
||||
assert IPA_SERVER_UPRGADE_LOG in install_msg
|
||||
assert IPA_SKIP_SIDGEN_LOG in install_msg
|
||||
+ assert SIDGEN_ERR_MSG not in install_msg
|
||||
|
||||
def test_ipa_migrate_prod_mode_dry_run(self, empty_log_file):
|
||||
"""
|
||||
@@ -509,7 +528,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
assert IPA_SERVER_UPRGADE_LOG in install_msg
|
||||
assert IPA_SIDGEN_LOG in install_msg
|
||||
|
||||
- def test_ipa_migrate_with_skip_schema_option_dry_run(self, empty_log_file):
|
||||
+ def test_ipa_migrate_skip_schema_dry_run(self, empty_log_file):
|
||||
"""
|
||||
This test checks that ipa-migrate tool works
|
||||
with -S(schema) options in stage mode
|
||||
@@ -532,7 +551,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
)
|
||||
assert SKIP_SCHEMA_MSG_LOG in install_msg
|
||||
|
||||
- def test_ipa_migrate_with_skip_config_option_dry_run(self, empty_log_file):
|
||||
+ def test_ipa_migrate_skip_config_dry_run(self, empty_log_file):
|
||||
"""
|
||||
This test checks that ipa-migrate tool works
|
||||
with -C(config) options in stage mode
|
||||
@@ -579,7 +598,7 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
)
|
||||
assert RESET_RANGE_LOG in install_msg
|
||||
|
||||
- def test_ipa_migrate_stage_mode_dry_override_schema(self, empty_log_file):
|
||||
+ def test_ipa_migrate_stage_mode_override_schema(self, empty_log_file):
|
||||
"""
|
||||
This test checks that -O option (override schema) works
|
||||
in dry mode
|
||||
@@ -601,70 +620,6 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
)
|
||||
assert SCHEMA_OVERRIDE_LOG in install_msg
|
||||
|
||||
- def test_ipa_migrate_stage_mode(self, empty_log_file):
|
||||
- """
|
||||
- This test checks that ipa-migrate is successful
|
||||
- in dry run mode
|
||||
- """
|
||||
- tasks.kinit_admin(self.master)
|
||||
- tasks.kinit_admin(self.replicas[0])
|
||||
- MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
|
||||
- MIGRATION_CONFIG_LOG_MSG = "Migrating configuration ...\n"
|
||||
- IPA_UPGRADE_LOG_MSG = (
|
||||
- "Running ipa-server-upgrade ... (this may take a while)\n"
|
||||
- )
|
||||
- SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
|
||||
- MIGRATION_COMPLETE_LOG_MSG = "Migration complete!\n"
|
||||
- result = run_migrate(
|
||||
- self.replicas[0],
|
||||
- "stage-mode",
|
||||
- self.master.hostname,
|
||||
- "cn=Directory Manager",
|
||||
- self.master.config.admin_password,
|
||||
- extra_args=['-n'],
|
||||
- )
|
||||
- install_msg = self.replicas[0].get_file_contents(
|
||||
- paths.IPA_MIGRATE_LOG, encoding="utf-8"
|
||||
- )
|
||||
- assert result.returncode == 0
|
||||
- assert MIGRATION_SCHEMA_LOG_MSG in install_msg
|
||||
- assert MIGRATION_CONFIG_LOG_MSG in install_msg
|
||||
- assert IPA_UPGRADE_LOG_MSG in install_msg
|
||||
- assert SIDGEN_TASK_LOG_MSG in install_msg
|
||||
- assert MIGRATION_COMPLETE_LOG_MSG in install_msg
|
||||
-
|
||||
- def test_ipa_migrate_prod_mode(self, empty_log_file):
|
||||
- """
|
||||
- This test checks that ipa-migrate is successful
|
||||
- in prod run mode
|
||||
- """
|
||||
- tasks.kinit_admin(self.master)
|
||||
- tasks.kinit_admin(self.replicas[0])
|
||||
- MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
|
||||
- MIGRATION_DATABASE_LOG_MSG = (
|
||||
- "Migrating database ... (this may take a while)\n"
|
||||
- )
|
||||
- IPA_UPGRADE_LOG_MSG = (
|
||||
- "Running ipa-server-upgrade ... (this may take a while)\n"
|
||||
- )
|
||||
- SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
|
||||
- result = run_migrate(
|
||||
- self.replicas[0],
|
||||
- "prod-mode",
|
||||
- self.master.hostname,
|
||||
- "cn=Directory Manager",
|
||||
- self.master.config.admin_password,
|
||||
- extra_args=['-n'],
|
||||
- )
|
||||
- install_msg = self.replicas[0].get_file_contents(
|
||||
- paths.IPA_MIGRATE_LOG, encoding="utf-8"
|
||||
- )
|
||||
- assert result.returncode == 0
|
||||
- assert MIGRATION_SCHEMA_LOG_MSG in install_msg
|
||||
- assert MIGRATION_DATABASE_LOG_MSG in install_msg
|
||||
- assert IPA_UPGRADE_LOG_MSG in install_msg
|
||||
- assert SIDGEN_TASK_LOG_MSG in install_msg
|
||||
-
|
||||
def test_ipa_migrate_with_bind_pwd_file_option(self, empty_log_file):
|
||||
"""
|
||||
This testcase checks that ipa-migrate tool
|
||||
@@ -801,6 +756,9 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
|
||||
@pytest.fixture()
|
||||
def modify_dns_zone(self):
|
||||
+ """
|
||||
+ This fixture adds dnszone and then removes the zone.
|
||||
+ """
|
||||
zone_name = 'ipatest.test'
|
||||
self.master.run_command(
|
||||
["ipa", "dnszone-add", zone_name, "--force"]
|
||||
@@ -844,6 +802,20 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
assert DNS_LOG2 in install_msg
|
||||
assert DNS_LOG3 in install_msg
|
||||
|
||||
+ def test_ipa_migrate_dns_forwardzone(self):
|
||||
+ """
|
||||
+ This testcase checks that DNS forwardzone is
|
||||
+ also migrated in prod-mode
|
||||
+ """
|
||||
+ zone_name = "forwardzone.test"
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "dnsforwardzone-show", zone_name]
|
||||
+ )
|
||||
+ assert 'Zone name: {}'.format(zone_name) in result.stdout_text
|
||||
+ assert 'Active zone: True' in result.stdout_text
|
||||
+ assert 'Zone forwarders: 10.11.12.13' in result.stdout_text
|
||||
+ assert 'Forward policy: first' in result.stdout_text
|
||||
+
|
||||
def test_ipa_migrate_version_option(self):
|
||||
"""
|
||||
The -V option has been removed.
|
||||
@@ -922,20 +894,179 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
assert result.returncode == 1
|
||||
assert ERR_MSG in result.stderr_text
|
||||
|
||||
- def test_ipa_hbac_rule_duplication(self):
|
||||
+
|
||||
+class TestIPAMigrationStageMode(MigrationTest):
|
||||
+ """
|
||||
+ Tests for ipa-migrate tool in stage mode
|
||||
+ """
|
||||
+ def test_ipa_migrate_stage_mode(self, empty_log_file):
|
||||
"""
|
||||
- This testcase checks that default hbac rules
|
||||
- are not duplicated on the local server when
|
||||
- ipa-migrate command is run.
|
||||
+ This test checks that ipa-migrate is successful
|
||||
+ in dry run mode
|
||||
"""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
|
||||
+ MIGRATION_CONFIG_LOG_MSG = "Migrating configuration ...\n"
|
||||
+ IPA_UPGRADE_LOG_MSG = (
|
||||
+ "Running ipa-server-upgrade ... (this may take a while)\n"
|
||||
+ )
|
||||
+ SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
|
||||
+ MIGRATION_COMPLETE_LOG_MSG = "Migration complete!\n"
|
||||
run_migrate(
|
||||
+ self.replicas[0],
|
||||
+ "stage-mode",
|
||||
+ self.master.hostname,
|
||||
+ "cn=Directory Manager",
|
||||
+ self.master.config.admin_password,
|
||||
+ extra_args=['-n'],
|
||||
+ )
|
||||
+ install_msg = self.replicas[0].get_file_contents(
|
||||
+ paths.IPA_MIGRATE_LOG, encoding="utf-8"
|
||||
+ )
|
||||
+ assert MIGRATION_SCHEMA_LOG_MSG in install_msg
|
||||
+ assert MIGRATION_CONFIG_LOG_MSG in install_msg
|
||||
+ assert IPA_UPGRADE_LOG_MSG in install_msg
|
||||
+ assert SIDGEN_TASK_LOG_MSG in install_msg
|
||||
+ assert MIGRATION_COMPLETE_LOG_MSG in install_msg
|
||||
+
|
||||
+ def test_ipa_migrate_stage_mode_new_user(self):
|
||||
+ """
|
||||
+ This testcase checks that when a new user is added and
|
||||
+ ipa-migrate is run in stage-mode, uid/gid of the
|
||||
+ migrated user is not preserved i.e we have different
|
||||
+ uid/gid for user on remote and local IPA server.
|
||||
+ """
|
||||
+ username = 'testuser4'
|
||||
+ base_dn = str(self.master.domain.basedn)
|
||||
+ LOG_MSG1 = (
|
||||
+ "DEBUG Resetting the DNA range for new entry: "
|
||||
+ "uid={},cn=users,cn=accounts,{}\n"
|
||||
+ ).format(username, base_dn)
|
||||
+ install_msg = self.replicas[0].get_file_contents(
|
||||
+ paths.IPA_MIGRATE_LOG, encoding="utf-8"
|
||||
+ )
|
||||
+ assert LOG_MSG1 not in install_msg
|
||||
+ tasks.clear_sssd_cache(self.master)
|
||||
+ self.master.run_command(['ipa', 'user-show', username])
|
||||
+ cmd1 = self.master.run_command(['id', username])
|
||||
+ tasks.clear_sssd_cache(self.replicas[0])
|
||||
+ self.replicas[0].run_command(['ipa', 'user-show', username])
|
||||
+ cmd2 = self.replicas[0].run_command(['id', username])
|
||||
+ assert cmd1.stdout_text != cmd2.stdout_text
|
||||
+
|
||||
+
|
||||
+class TestIPAMigrationProdMode(MigrationTest):
|
||||
+ """
|
||||
+ Tests for ipa-migrate tool in prod mode
|
||||
+ """
|
||||
+ def test_ipa_migrate_prod_mode(self, empty_log_file):
|
||||
+ """
|
||||
+ This test checks that ipa-migrate is successful
|
||||
+ in prod run mode
|
||||
+ """
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n"
|
||||
+ MIGRATION_DATABASE_LOG_MSG = (
|
||||
+ "Migrating database ... (this may take a while)\n"
|
||||
+ )
|
||||
+ IPA_UPGRADE_LOG_MSG = (
|
||||
+ "Running ipa-server-upgrade ... (this may take a while)\n"
|
||||
+ )
|
||||
+ SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n"
|
||||
+ result = run_migrate(
|
||||
self.replicas[0],
|
||||
"prod-mode",
|
||||
self.master.hostname,
|
||||
"cn=Directory Manager",
|
||||
self.master.config.admin_password,
|
||||
- extra_args=['-n']
|
||||
+ extra_args=['-n'],
|
||||
)
|
||||
+ install_msg = self.replicas[0].get_file_contents(
|
||||
+ paths.IPA_MIGRATE_LOG, encoding="utf-8"
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ assert MIGRATION_SCHEMA_LOG_MSG in install_msg
|
||||
+ assert MIGRATION_DATABASE_LOG_MSG in install_msg
|
||||
+ assert IPA_UPGRADE_LOG_MSG in install_msg
|
||||
+ assert SIDGEN_TASK_LOG_MSG in install_msg
|
||||
+
|
||||
+ def test_ipa_migrate_prod_mode_hbac_rule(self):
|
||||
+ """
|
||||
+ This testcase checks that hbac rule is migrated from
|
||||
+ remote server to local server in prod mode.
|
||||
+ """
|
||||
+ hbac_rule_name1 = 'test1'
|
||||
+ hbac_rule_name2 = 'testuser_sshd'
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ cmd1 = self.replicas[0].run_command(
|
||||
+ ["ipa", "hbacrule-find", hbac_rule_name1])
|
||||
+ cmd2 = self.replicas[0].run_command(
|
||||
+ ["ipa", "hbacrule-find", hbac_rule_name2])
|
||||
+ assert hbac_rule_name1 in cmd1.stdout_text
|
||||
+ assert hbac_rule_name2 in cmd2.stdout_text
|
||||
+
|
||||
+ def test_ipa_migrate_prod_mode_sudo_rule(self):
|
||||
+ """
|
||||
+ This testcase checks that sudo cmd and rules are
|
||||
+ migrated from remote server to local server in prod mode.
|
||||
+ """
|
||||
+ sudorule = 'readfiles'
|
||||
+ sudocmd = '/usr/bin/less'
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ cmd1 = self.replicas[0].run_command(
|
||||
+ ["ipa", "sudorule-find", sudorule])
|
||||
+ cmd2 = self.replicas[0].run_command(
|
||||
+ ["ipa", "sudocmd-find", sudocmd])
|
||||
+ assert 'Rule name: readfiles\n' in cmd1.stdout_text
|
||||
+ assert 'Sudo Command: /usr/bin/less\n' in cmd2.stdout_text
|
||||
+
|
||||
+ def test_ipa_migrate_prod_mode_new_user_sid(self):
|
||||
+ """
|
||||
+ This testcase checks that in prod-mode uid/gid of the
|
||||
+ migrated user is preserved i.e we have same
|
||||
+ uid/gid for user on remote and local IPA server.
|
||||
+ """
|
||||
+ username = 'testuser4'
|
||||
+ tasks.clear_sssd_cache(self.master)
|
||||
+ result1 = self.master.run_command(['id', username])
|
||||
+ tasks.clear_sssd_cache(self.replicas[0])
|
||||
+ result2 = self.replicas[0].run_command(['id', username])
|
||||
+ assert result1.stdout_text == result2.stdout_text
|
||||
+
|
||||
+ def test_check_vault_is_not_migrated(self):
|
||||
+ """
|
||||
+ This testcase checks that vault is
|
||||
+ not migrated
|
||||
+ """
|
||||
+ vault_name = "testvault"
|
||||
+ CMD_OUTPUT = "Number of entries returned 0"
|
||||
+ cmd = self.replicas[0].run_command(
|
||||
+ ["ipa", "vault-find", vault_name], raiseonerr=False)
|
||||
+ assert cmd.returncode != 0
|
||||
+ assert CMD_OUTPUT in cmd.stdout_text
|
||||
+
|
||||
+ def test_ipa_migrate_subids(self):
|
||||
+ """
|
||||
+ This testcase checks that subids for users are migrated
|
||||
+ to the local server from the remote server
|
||||
+ """
|
||||
+ user_name = 'admin'
|
||||
+ CMD_MSG = "1 subordinate id matched"
|
||||
+ cmd = self.replicas[0].run_command(
|
||||
+ ['ipa', 'subid-find',
|
||||
+ '--owner', user_name]
|
||||
+ )
|
||||
+ assert cmd.returncode == 0
|
||||
+ assert CMD_MSG in cmd.stdout_text
|
||||
+
|
||||
+ def test_ipa_hbac_rule_duplication(self):
|
||||
+ """
|
||||
+ This testcase checks that default hbac rules
|
||||
+ are not duplicated on the local server when
|
||||
+ ipa-migrate command is run.
|
||||
+ """
|
||||
result = self.replicas[0].run_command(
|
||||
['ipa', 'hbacrule-find']
|
||||
)
|
||||
@@ -946,3 +1077,192 @@ class TestIPAMigrateScenario1(IntegrationTest):
|
||||
count = Counter(line)
|
||||
assert count.get('Rule name: allow_all') < 2
|
||||
assert count.get('Rule name: allow_systemd-user') < 2
|
||||
+
|
||||
+ def test_ipa_migrate_otptoken(self):
|
||||
+ """
|
||||
+ This testcase checks that the otptoken
|
||||
+ is migrated for the user.
|
||||
+ """
|
||||
+ owner = "testuser1"
|
||||
+ CMD_OUTPUT = "1 OTP token matched"
|
||||
+ result = self.replicas[0].run_command([
|
||||
+ "ipa", "otptoken-find"
|
||||
+ ])
|
||||
+ assert CMD_OUTPUT in result.stdout_text
|
||||
+ assert 'Type: TOTP' in result.stdout_text
|
||||
+ assert 'Owner: {}'.format(owner) in result.stdout_text
|
||||
+
|
||||
+ def test_ipa_migrate_check_passkey_config(self):
|
||||
+ """
|
||||
+ This testcase checks that passkey config
|
||||
+ is migrated
|
||||
+ """
|
||||
+ CMD_OUTPUT = "Require user verification: False"
|
||||
+ result = self.replicas[0].run_command([
|
||||
+ "ipa", "passkeyconfig-show"
|
||||
+ ])
|
||||
+ assert CMD_OUTPUT in result.stdout_text
|
||||
+
|
||||
+ def test_ipa_migrate_check_service_status(self):
|
||||
+ """
|
||||
+ This testcase checks that ipactl and sssd
|
||||
+ services are running post ipa-migrate tool
|
||||
+ successful runs completed
|
||||
+ """
|
||||
+ cmd1 = self.replicas[0].run_command([
|
||||
+ "ipactl", "status"
|
||||
+ ])
|
||||
+ assert cmd1.returncode == 0
|
||||
+ cmd2 = self.replicas[0].run_command([
|
||||
+ "systemctl", "status", "sssd"
|
||||
+ ])
|
||||
+ assert cmd2.returncode == 0
|
||||
+
|
||||
+ def test_custom_idrange_is_migrated(self):
|
||||
+ """
|
||||
+ This testcase checks that custom idrange is migrated
|
||||
+ from remote server to local server in production
|
||||
+ mode.
|
||||
+ """
|
||||
+ range_name = "testrange"
|
||||
+ CMD_OUTPUT = (
|
||||
+ "---------------\n"
|
||||
+ "1 range matched\n"
|
||||
+ "---------------\n"
|
||||
+ " Range name: testrange\n"
|
||||
+ " First Posix ID of the range: 10000\n"
|
||||
+ " Number of IDs in the range: 10000\n"
|
||||
+ " First RID of the corresponding RID range: 300000\n"
|
||||
+ " First RID of the secondary RID range: 400000\n"
|
||||
+ " Range type: local domain range\n"
|
||||
+ "----------------------------\n"
|
||||
+ "Number of entries returned 1\n"
|
||||
+ "----------------------------\n"
|
||||
+ )
|
||||
+ cmd = self.replicas[0].run_command(
|
||||
+ ["ipa", "idrange-find", range_name])
|
||||
+ assert CMD_OUTPUT in cmd.stdout_text
|
||||
+
|
||||
+ def test_automountlocation_is_migrated(self):
|
||||
+ """
|
||||
+ This testcase checks that automount location/maps
|
||||
+ and keys are migrated.
|
||||
+ """
|
||||
+ base_dn = str(self.master.domain.basedn)
|
||||
+ automount_cn = "automount"
|
||||
+ loc_name = "baltimore"
|
||||
+ auto_map_name = "auto.share"
|
||||
+ DEBUG_LOG = (
|
||||
+ "Added entry: cn={},cn={},{}\n"
|
||||
+ ).format(loc_name, automount_cn, base_dn)
|
||||
+ CMD1_OUTPUT = (
|
||||
+ " Location: baltimore\n"
|
||||
+ )
|
||||
+ CMD2_OUTPUT = (
|
||||
+ " Map: auto.share\n"
|
||||
+ )
|
||||
+ CMD3_OUTPUT = (
|
||||
+ "-----------------------\n"
|
||||
+ "1 automount key matched\n"
|
||||
+ "-----------------------\n"
|
||||
+ " Key: sub\n"
|
||||
+ " Mount information: -fstype=autofs ldap:auto.man\n"
|
||||
+ )
|
||||
+ cmd1 = self.replicas[0].run_command(
|
||||
+ ["ipa", "automountlocation-show", loc_name])
|
||||
+ cmd2 = self.replicas[0].run_command(
|
||||
+ ["ipa", "automountmap-find", loc_name])
|
||||
+ cmd3 = self.replicas[0].run_command(
|
||||
+ ["ipa", "automountkey-find", loc_name, auto_map_name]
|
||||
+ )
|
||||
+ install_msg = self.replicas[0].get_file_contents(
|
||||
+ paths.IPA_MIGRATE_LOG, encoding="utf-8"
|
||||
+ )
|
||||
+ assert CMD1_OUTPUT in cmd1.stdout_text
|
||||
+ assert CMD2_OUTPUT in cmd2.stdout_text
|
||||
+ assert CMD3_OUTPUT in cmd3.stdout_text
|
||||
+ assert DEBUG_LOG in install_msg
|
||||
+
|
||||
+
|
||||
+class TestIPAMigrationWithADtrust(IntegrationTest):
|
||||
+ """
|
||||
+ Test for ipa-migrate tool with IPA Master having trust setup
|
||||
+ with Windows AD.
|
||||
+ """
|
||||
+ topology = "line"
|
||||
+ num_ad_domains = 1
|
||||
+ num_replicas = 1
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ tasks.install_master(
|
||||
+ cls.master, setup_dns=True, extra_args=['--no-dnssec-validation']
|
||||
+ )
|
||||
+ cls.ad = cls.ads[0]
|
||||
+ cls.ad_domain = cls.ad.domain.name
|
||||
+ tasks.install_adtrust(cls.master)
|
||||
+ tasks.configure_dns_for_trust(cls.master, cls.ad)
|
||||
+ tasks.establish_trust_with_ad(cls.master, cls.ad.domain.name)
|
||||
+
|
||||
+ def test_install_local_server(self):
|
||||
+ """
|
||||
+ This test installs local IPA Server() i.e new IPA server with
|
||||
+ the same realm and domain name that will receive the migration data.
|
||||
+ """
|
||||
+ tasks.install_master(
|
||||
+ self.replicas[0], setup_dns=True,
|
||||
+ extra_args=['--no-dnssec-validation']
|
||||
+ )
|
||||
+ tasks.install_adtrust(self.replicas[0])
|
||||
+
|
||||
+ def test_check_ad_attributes_migrate_prod_mode(self):
|
||||
+ """
|
||||
+ This test checks that IPA-AD trust related attributes
|
||||
+ are migrated to local server.
|
||||
+ """
|
||||
+ result = run_migrate(
|
||||
+ self.replicas[0],
|
||||
+ "prod-mode",
|
||||
+ self.master.hostname,
|
||||
+ "cn=Directory Manager",
|
||||
+ self.master.config.admin_password,
|
||||
+ extra_args=['-n']
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ trust1 = self.master.run_command(
|
||||
+ ['ipa', 'trust-show', self.ad_domain]
|
||||
+ ).stdout_text
|
||||
+ trust2 = self.replicas[0].run_command(
|
||||
+ ['ipa', 'trust-show', self.ad_domain]).stdout_text
|
||||
+ assert trust1 == trust2
|
||||
+
|
||||
+ def test_check_domain_sid_is_migrated(self):
|
||||
+ """
|
||||
+ This testcase checks that domain sid is
|
||||
+ migrated from a remote server having trust with AD
|
||||
+ to local server and is displayed in the
|
||||
+ ipa trustconfig-show command
|
||||
+ """
|
||||
+ regexp = (r'Security Identifier: (.*)$')
|
||||
+ cmd1 = self.master.run_command(["ipa", "trustconfig-show"])
|
||||
+ sid1 = re.findall(regexp, cmd1.stdout_text, re.MULTILINE)
|
||||
+ cmd2 = self.replicas[0].run_command(
|
||||
+ ["ipa", "trustconfig-show"]
|
||||
+ )
|
||||
+ sid2 = re.findall(regexp, cmd2.stdout_text, re.MULTILINE)
|
||||
+ assert sid1 == sid2
|
||||
+
|
||||
+ def test_check_ad_idrange_is_migrated(self):
|
||||
+ """
|
||||
+ This testcase checks AD idrange is migrated
|
||||
+ from remote IPA server having trust with AD
|
||||
+ to local IPA server
|
||||
+ """
|
||||
+ ad_domain_name = self.ad.domain.name.upper()
|
||||
+ cmd1 = self.master.run_command(
|
||||
+ ["ipa", "idrange-show", ad_domain_name + "_id_range"]
|
||||
+ )
|
||||
+ cmd2 = self.replicas[0].run_command(
|
||||
+ ["ipa", "idrange-show", ad_domain_name + "_id_range"]
|
||||
+ )
|
||||
+ assert cmd1.stdout_text == cmd2.stdout_text
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
From c64c098e1d0ae492499caa83a1b73532da511f84 Mon Sep 17 00:00:00 2001
|
||||
From: Carla Martinez <carlmart@redhat.com>
|
||||
Date: Tue, 29 Oct 2024 15:23:55 +0100
|
||||
Subject: [PATCH] Fix: 'Organization' field in Okta not required
|
||||
|
||||
Although the 'Organization' field is not required
|
||||
when using the Okta template, the WebUI requires it
|
||||
in order to create a new IDP. If this is not provided,
|
||||
an error is shown.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9687
|
||||
Signed-off-by: Carla Martinez <carlmart@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
install/ui/src/freeipa/idp.js | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/install/ui/src/freeipa/idp.js b/install/ui/src/freeipa/idp.js
|
||||
index ada09c0754f5a51575831e127deb81d1f27f44d1..04daad591a8e94ea9b8c146c12e0c84aaad6cee4 100644
|
||||
--- a/install/ui/src/freeipa/idp.js
|
||||
+++ b/install/ui/src/freeipa/idp.js
|
||||
@@ -41,7 +41,7 @@ idp.templates = [
|
||||
fields: ['ipaidporg']},
|
||||
{ value: 'okta',
|
||||
label: text.get('@i18n:objects.idp.template_okta'),
|
||||
- fields: ['ipaidporg', 'ipaidpbaseurl']}
|
||||
+ fields: ['ipaidpbaseurl']}
|
||||
];
|
||||
|
||||
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
From 0bf2a549a8a858c393ef59487bc1d395e5535c07 Mon Sep 17 00:00:00 2001
|
||||
From: Stanislav Levin <slev@altlinux.org>
|
||||
Date: Tue, 24 Feb 2026 19:44:51 +0300
|
||||
Subject: [PATCH] ipatest: make tests compatible with Pytest 9
|
||||
|
||||
https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function
|
||||
|
||||
> Applying a mark to a fixture function never had any effect, but it is a common user error.
|
||||
|
||||
Move marks from the fixture to test class.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9950
|
||||
Signed-off-by: Stanislav Levin <slev@altlinux.org>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
---
|
||||
ipatests/test_ipapython/test_ldap_cache.py | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_ipapython/test_ldap_cache.py b/ipatests/test_ipapython/test_ldap_cache.py
|
||||
index c960db027..49724ad2f 100644
|
||||
--- a/ipatests/test_ipapython/test_ldap_cache.py
|
||||
+++ b/ipatests/test_ipapython/test_ldap_cache.py
|
||||
@@ -20,8 +20,6 @@ def hits_and_misses(cache, hits, misses):
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
-@pytest.mark.tier1
|
||||
-@pytest.mark.needs_ipaapi
|
||||
def class_cache(request):
|
||||
cache = ipaldap.LDAPCache(api.env.ldap_uri)
|
||||
hits_and_misses(cache, 0, 0)
|
||||
@@ -56,6 +54,7 @@ def class_cache(request):
|
||||
@pytest.mark.usefixtures('class_cache')
|
||||
@pytest.mark.skip_ipaclient_unittest
|
||||
@pytest.mark.needs_ipaapi
|
||||
+@pytest.mark.tier1
|
||||
class TestLDAPCache:
|
||||
|
||||
def test_one(self):
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,154 @@
|
||||
From f60df430602db1e3949fa67f3744d40bd9b1d971 Mon Sep 17 00:00:00 2001
|
||||
From: Jay Gondaliya <jgondali@redhat.com>
|
||||
Date: Fri, 27 Feb 2026 15:38:54 +0530
|
||||
Subject: [PATCH] ipatests: Add ipa-selfservice BZ tests to xmlrpc
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
-Convert Bugzilla regression tests (BZ 772106, 772675, 747730, 747741, 747720, 747722) from bash to Python and add them to the existing selfservice test file as the TestSelfserviceMisc Declarative class.
|
||||
-Tests verify that --raw output, empty permissions/attrs, and invalid attrs do not cause internal errors or accidental ACI deletion.
|
||||
-Use a single selfservice rule (selfservice1) across all BZ tests instead of creating and deleting a separate rule per test case, reducing churn and keeping the tests fast.
|
||||
-Drop BZ 747693 (selfservice-find --raw) as it is already covered by the existing "Search for 'testself' with --raw" test in the main test_selfservice CRUD class (test 0011).
|
||||
|
||||
Signed-off-by: Jay Gondaliya jgondali@redhat.com
|
||||
Fixes: https://pagure.io/freeipa/issue/9945
|
||||
Assisted-by: Claude noreply@anthropic.com
|
||||
|
||||
Continuation of PR #8190
|
||||
|
||||
Fixes made:
|
||||
-Fixed lambda expected checkers — replaced defensive .get("result", {}) chains with direct output["result"] key access.
|
||||
-Removed redundant delete test case — dropped explicit selfservice_del test, relying solely on cleanup_commands.
|
||||
-Renamed class TestSelfserviceMisc → test_selfservice_misc.
|
||||
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
.../test_xmlrpc/test_selfservice_plugin.py | 106 +++++++++++++++++-
|
||||
1 file changed, 105 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipatests/test_xmlrpc/test_selfservice_plugin.py b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
index 02aea35e1..4c5059519 100644
|
||||
--- a/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
@@ -28,7 +28,6 @@ import pytest
|
||||
selfservice1 = u'testself'
|
||||
invalid_selfservice1 = u'bad+name'
|
||||
|
||||
-
|
||||
@pytest.mark.tier1
|
||||
class test_selfservice(Declarative):
|
||||
|
||||
@@ -290,3 +289,108 @@ class test_selfservice(Declarative):
|
||||
),
|
||||
|
||||
]
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+class test_selfservice_misc(Declarative):
|
||||
+ """Bugzilla regression tests for selfservice plugin."""
|
||||
+
|
||||
+ cleanup_commands = [
|
||||
+ ("selfservice_del", [selfservice1], {}),
|
||||
+ ]
|
||||
+
|
||||
+ tests = [
|
||||
+ # BZ 772106: selfservice-add with --raw must not return internal error
|
||||
+ dict(
|
||||
+ desc="Create %r with --raw for BZ 772106" % selfservice1,
|
||||
+ command=(
|
||||
+ "selfservice_add",
|
||||
+ [selfservice1],
|
||||
+ dict(attrs=["l"], raw=True),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=selfservice1,
|
||||
+ summary='Added selfservice "%s"' % selfservice1,
|
||||
+ result={
|
||||
+ "aci": '(targetattr = "l")(version 3.0;acl '
|
||||
+ '"selfservice:%s";allow (write) '
|
||||
+ 'userdn = "ldap:///self";)' % selfservice1,
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+ # BZ 772675: selfservice-mod with --raw must not return internal error
|
||||
+ dict(
|
||||
+ desc="Modify %r with --raw for BZ 772675" % selfservice1,
|
||||
+ command=(
|
||||
+ "selfservice_mod",
|
||||
+ [selfservice1],
|
||||
+ dict(attrs=["mobile"], raw=True),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=selfservice1,
|
||||
+ summary='Modified selfservice "%s"' % selfservice1,
|
||||
+ result={
|
||||
+ "aci": '(targetattr = "mobile")(version 3.0;acl '
|
||||
+ '"selfservice:%s";allow (write) '
|
||||
+ 'userdn = "ldap:///self";)' % selfservice1,
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+ # BZ 747730: selfservice-mod --permissions="" must not delete the entry
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ "Modify %r with empty permissions for BZ 747730"
|
||||
+ % selfservice1
|
||||
+ ),
|
||||
+ command=(
|
||||
+ "selfservice_mod",
|
||||
+ [selfservice1],
|
||||
+ dict(permissions=""),
|
||||
+ ),
|
||||
+ expected=lambda got, output: True,
|
||||
+ ),
|
||||
+ dict(
|
||||
+ desc="Verify %r still exists after BZ 747730" % selfservice1,
|
||||
+ command=("selfservice_show", [selfservice1], {}),
|
||||
+ expected=lambda got, output: (
|
||||
+ got is None
|
||||
+ and output["result"]["aciname"] == selfservice1
|
||||
+ ),
|
||||
+ ),
|
||||
+ # BZ 747741: selfservice-mod --attrs=badattrs must not delete the entry
|
||||
+ dict(
|
||||
+ desc="Modify %r with bad attrs for BZ 747741" % selfservice1,
|
||||
+ command=(
|
||||
+ "selfservice_mod",
|
||||
+ [selfservice1],
|
||||
+ dict(attrs=["badattrs"]),
|
||||
+ ),
|
||||
+ expected=lambda got, output: True,
|
||||
+ ),
|
||||
+ dict(
|
||||
+ desc="Verify %r still exists after BZ 747741" % selfservice1,
|
||||
+ command=("selfservice_show", [selfservice1], {}),
|
||||
+ expected=lambda got, output: (
|
||||
+ got is None
|
||||
+ and output["result"]["aciname"] == selfservice1
|
||||
+ ),
|
||||
+ ),
|
||||
+ # BZ 747720: selfservice-find --permissions="" must not return
|
||||
+ # internal error
|
||||
+ dict(
|
||||
+ desc="BZ 747720: selfservice-find with empty permissions",
|
||||
+ command=("selfservice_find", [], dict(permissions="")),
|
||||
+ expected=lambda got, output: (
|
||||
+ got is None and isinstance(output["result"], (list, tuple))
|
||||
+ ),
|
||||
+ ),
|
||||
+ # BZ 747722: selfservice-find --attrs="" must not return
|
||||
+ # internal error
|
||||
+ dict(
|
||||
+ desc="BZ 747722: selfservice-find with empty attrs",
|
||||
+ command=("selfservice_find", [], dict(attrs="")),
|
||||
+ expected=lambda got, output: (
|
||||
+ got is None and isinstance(output["result"], (list, tuple))
|
||||
+ ),
|
||||
+ ),
|
||||
+ ]
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
From baa9fc3e3e2f6b39db5ec465c92dc597cd5399b9 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 12 Nov 2024 16:44:46 +0100
|
||||
Subject: [PATCH] ipatests: install master with allow-zone-overlap
|
||||
|
||||
In the IPA to IPA migration tests, install the destination master
|
||||
with --setup-dns --allow-zone-overlap to allow installation
|
||||
even if the zone is already served by the source master.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9697
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Sudhir Menon <sumenon@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_ipa_ipa_migration.py | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
index d852ca63a6b3a7e7118d66ce1cd9bb98e56f1a73..0c637a0141d95f34f951c60a9648adf8e87eaa63 100644
|
||||
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
@@ -345,7 +345,8 @@ class MigrationTest(IntegrationTest):
|
||||
tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
|
||||
prepare_ipa_server(cls.master)
|
||||
tasks.install_client(cls.master, cls.clients[0], nameservers=None)
|
||||
- tasks.install_master(cls.replicas[0], setup_dns=True, setup_kra=True)
|
||||
+ tasks.install_master(cls.replicas[0], setup_dns=True, setup_kra=True,
|
||||
+ extra_args=['--allow-zone-overlap'])
|
||||
|
||||
|
||||
class TestIPAMigrateCLIOptions(MigrationTest):
|
||||
@@ -1211,7 +1212,7 @@ class TestIPAMigrationWithADtrust(IntegrationTest):
|
||||
"""
|
||||
tasks.install_master(
|
||||
self.replicas[0], setup_dns=True,
|
||||
- extra_args=['--no-dnssec-validation']
|
||||
+ extra_args=['--no-dnssec-validation', '--allow-zone-overlap']
|
||||
)
|
||||
tasks.install_adtrust(self.replicas[0])
|
||||
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
From a5ff0a2261e2df7b2157e6509cdbecf65033e578 Mon Sep 17 00:00:00 2001
|
||||
From 58239f9fbe33408b5cb5c52ba5132f6deb6b8f40 Mon Sep 17 00:00:00 2001
|
||||
From: David Hanina <dhanina@redhat.com>
|
||||
Date: Tue, 10 Mar 2026 10:26:33 +0100
|
||||
Subject: [PATCH] Allow 32bit gid
|
||||
@ -7,14 +7,13 @@ We should allow 32bit groups, by setting maxvalue we allow that.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9953
|
||||
Signed-off-by: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
---
|
||||
ipaserver/plugins/group.py | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/ipaserver/plugins/group.py b/ipaserver/plugins/group.py
|
||||
index f05a39f69ec3eb3257909bd61b42a9a21212c14d..308e5458c1d00be34753f42675367a7307331514 100644
|
||||
index f05a39f69..308e5458c 100644
|
||||
--- a/ipaserver/plugins/group.py
|
||||
+++ b/ipaserver/plugins/group.py
|
||||
@@ -26,6 +26,7 @@ import re
|
||||
@ -1,148 +0,0 @@
|
||||
From c71e12e902b3912c31245d46ad6f2c2ddee01126 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Tue, 1 Oct 2024 11:28:28 +0300
|
||||
Subject: [PATCH] selinux: allow Cockpit to use HTTP keytab on IPA servers
|
||||
|
||||
Cockpit can use GSSAPI authentication and has pretty good definition of
|
||||
how to enable it: https://cockpit-project.org/guide/latest/sso.html.
|
||||
These instructions work on IPA clients but they cannot be used on IPA
|
||||
servers because IPA framework already owns HTTP/.. Kerberos service and
|
||||
its keytab.
|
||||
|
||||
Luckily, there are two changes that need to be done to enable Cockpit
|
||||
single sign-on with GSSAPI on IPA servers:
|
||||
|
||||
- create a symlink /etc/cockpit/krb5.keytab to
|
||||
/var/lib/ipa/gssproxy/http.keytab
|
||||
|
||||
- add SELinux policy to allow cockpit_session_t to operate on
|
||||
/var/lib/ipa/gssproxy/http.keytab file
|
||||
|
||||
For existing installation an upgrade process would restore SELinux
|
||||
context of the http.keytab file to the new value.
|
||||
|
||||
Note that Cockpit documentation above also talks about Kerberos service
|
||||
modifications to enable delegation. These modifications should not be
|
||||
done for IPA servers' HTTP services, as these services are already
|
||||
enabled to handle delegation.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9675
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/server/upgrade.py | 1 +
|
||||
selinux/ipa.fc | 2 ++
|
||||
selinux/ipa.if | 24 ++++++++++++++++++++++++
|
||||
selinux/ipa.te | 19 +++++++++++++++++++
|
||||
4 files changed, 46 insertions(+)
|
||||
|
||||
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
|
||||
index 31d4f8398cfb0251cc59ada909eb55635b83e960..d5c466ee2f905eafd15663fef46d052ade30d742 100644
|
||||
--- a/ipaserver/install/server/upgrade.py
|
||||
+++ b/ipaserver/install/server/upgrade.py
|
||||
@@ -1124,6 +1124,7 @@ def update_http_keytab(http):
|
||||
paths.OLD_IPA_KEYTAB, e
|
||||
)
|
||||
http.keytab_user.chown(http.keytab)
|
||||
+ tasks.restore_context(http.keytab)
|
||||
|
||||
|
||||
def ds_enable_sidgen_extdom_plugins(ds):
|
||||
diff --git a/selinux/ipa.fc b/selinux/ipa.fc
|
||||
index 47bd19ba77418cad1f0904dc4a9a35ce9d6ff9d2..15e8e41aa50228ff560e338044240b46bc24cc40 100644
|
||||
--- a/selinux/ipa.fc
|
||||
+++ b/selinux/ipa.fc
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
/var/lib/ipa(/.*)? gen_context(system_u:object_r:ipa_var_lib_t,s0)
|
||||
|
||||
+/var/lib/ipa/gssproxy/http.keytab -- gen_context(system_u:object_r:ipa_http_keytab_t,s0)
|
||||
+
|
||||
/var/log/ipa(/.*)? gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
|
||||
/var/log/ipabackup.log -- gen_context(system_u:object_r:ipa_log_t,s0)
|
||||
diff --git a/selinux/ipa.if b/selinux/ipa.if
|
||||
index 8c47e7963af92b1ddcd59d92aa45d6b8e9c0c6cc..8f3147e10bd294665dd41e1c1f99c993d9699d20 100644
|
||||
--- a/selinux/ipa.if
|
||||
+++ b/selinux/ipa.if
|
||||
@@ -155,6 +155,7 @@ interface(`ipa_manage_log',`
|
||||
########################################
|
||||
## <summary>
|
||||
## Allow domain to manage ipa lib files/dirs.
|
||||
+## This includes reading ipa_http_keytab_t files.
|
||||
## </summary>
|
||||
## <param name="domain">
|
||||
## <summary>
|
||||
@@ -164,10 +165,33 @@ interface(`ipa_manage_log',`
|
||||
#
|
||||
interface(`ipa_read_lib',`
|
||||
gen_require(`
|
||||
+ type ipa_http_keytab_t;
|
||||
type ipa_var_lib_t;
|
||||
')
|
||||
|
||||
read_files_pattern($1, ipa_var_lib_t, ipa_var_lib_t)
|
||||
+ read_files_pattern($1, ipa_http_keytab_t, ipa_http_keytab_t)
|
||||
+ list_dirs_pattern($1, ipa_var_lib_t, ipa_var_lib_t)
|
||||
+')
|
||||
+
|
||||
+########################################
|
||||
+## <summary>
|
||||
+## Allow domain to manage ipa HTTP keytab file.
|
||||
+## This includes reading ipa_var_lib_t directories.
|
||||
+## </summary>
|
||||
+## <param name="domain">
|
||||
+## <summary>
|
||||
+## Domain allowed access.
|
||||
+## </summary>
|
||||
+## </param>
|
||||
+#
|
||||
+interface(`ipa_read_http_keytab',`
|
||||
+ gen_require(`
|
||||
+ type ipa_http_keytab_t;
|
||||
+ type ipa_var_lib_t;
|
||||
+ ')
|
||||
+
|
||||
+ read_files_pattern($1, ipa_http_keytab_t, ipa_http_keytab_t)
|
||||
list_dirs_pattern($1, ipa_var_lib_t, ipa_var_lib_t)
|
||||
')
|
||||
|
||||
diff --git a/selinux/ipa.te b/selinux/ipa.te
|
||||
index 2546a9bd9468200185c484974a9e71f16f89de71..e4ce66687a48b27e85591cdd8352f7cac94d3151 100644
|
||||
--- a/selinux/ipa.te
|
||||
+++ b/selinux/ipa.te
|
||||
@@ -43,6 +43,9 @@ logging_log_file(ipa_log_t)
|
||||
type ipa_var_lib_t;
|
||||
files_type(ipa_var_lib_t)
|
||||
|
||||
+type ipa_http_keytab_t;
|
||||
+files_type(ipa_http_keytab_t)
|
||||
+
|
||||
type ipa_var_run_t;
|
||||
files_pid_file(ipa_var_run_t)
|
||||
|
||||
@@ -516,3 +519,19 @@ optional_policy(`
|
||||
')
|
||||
allow certmonger_t pki_tomcat_etc_rw_t:file { getattr ioctl open read };
|
||||
')
|
||||
+
|
||||
+# gssproxy needs to read http keytab
|
||||
+optional_policy(`
|
||||
+ gen_require(`
|
||||
+ type gssproxy_t;
|
||||
+ ')
|
||||
+ ipa_read_http_keytab(gssproxy_t)
|
||||
+')
|
||||
+
|
||||
+# Allow Cockpit to use HTTP keytab on IPA servers for GSSAPI authentication
|
||||
+optional_policy(`
|
||||
+ gen_require(`
|
||||
+ type cockpit_session_t;
|
||||
+ ')
|
||||
+ ipa_read_http_keytab(cockpit_session_t)
|
||||
+')
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -1,94 +0,0 @@
|
||||
From 0dadcbb4ac9f6142b5130f025f64d918d6f208a9 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Tue, 8 Oct 2024 10:25:08 +0300
|
||||
Subject: [PATCH] Minimal test for Cockpit integration on IPA master
|
||||
|
||||
Add a test to share HTTP service keytab on IPA master between IPA and
|
||||
Cockpit. The test configures Cockpit with IPA CA-issued certificate and
|
||||
allows Cockpit to access IPA HTTP service keytab for authentication.
|
||||
|
||||
The test then attempts to authenticate with GSSAPI as admin user. A
|
||||
successful result is when we receive CSRF token from the Cockpit as
|
||||
the result of this authentication. This means we have logged in
|
||||
successfully with Kerberos.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9675
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_cockpit.py | 61 +++++++++++++++++++++++
|
||||
1 file changed, 61 insertions(+)
|
||||
create mode 100644 ipatests/test_integration/test_cockpit.py
|
||||
|
||||
diff --git a/ipatests/test_integration/test_cockpit.py b/ipatests/test_integration/test_cockpit.py
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..cdc96170a116536c7aa00be78cc4e0225804e21c
|
||||
--- /dev/null
|
||||
+++ b/ipatests/test_integration/test_cockpit.py
|
||||
@@ -0,0 +1,61 @@
|
||||
+#
|
||||
+# Copyright (C) 2024 FreeIPA Contributors see COPYING for license
|
||||
+#
|
||||
+
|
||||
+from __future__ import absolute_import
|
||||
+
|
||||
+import time
|
||||
+from ipatests.pytest_ipa.integration import tasks
|
||||
+from ipatests.test_integration.base import IntegrationTest
|
||||
+from ipaplatform.paths import paths
|
||||
+
|
||||
+
|
||||
+class TestCockpitIntegration(IntegrationTest):
|
||||
+ topology = "line"
|
||||
+ reqcert = '/etc/cockpit/ws-certs.d/99-cockpit.cert'
|
||||
+ reqkey = '/etc/cockpit/ws-certs.d/99-cockpit.key'
|
||||
+ symlink = '/etc/cockpit/krb5.keytab'
|
||||
+
|
||||
+ @classmethod
|
||||
+ def uninstall(cls, mh):
|
||||
+ cls.master.run_command(['ipa-getcert', 'stop-tracking', '-f',
|
||||
+ cls.reqcert], raiseonerr=False)
|
||||
+ cls.master.run_command(['rm', '-f', cls.symlink], raiseonerr=False)
|
||||
+ cls.master.run_command(['systemctl', 'disable', '--now',
|
||||
+ 'cockpit.socket'])
|
||||
+ super(TestCockpitIntegration, cls).uninstall(mh)
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ master = cls.master
|
||||
+
|
||||
+ # Install Cockpit and configure it to use IPA certificate and keytab
|
||||
+ master.run_command(['dnf', 'install', '-y', 'cockpit', 'curl'],
|
||||
+ raiseonerr=False)
|
||||
+
|
||||
+ super(TestCockpitIntegration, cls).install(mh)
|
||||
+
|
||||
+ master.run_command(['ipa-getcert', 'request', '-f', cls.reqcert, '-k',
|
||||
+ cls.reqkey, '-D', cls.master.hostname, '-K',
|
||||
+ 'host/' + cls.master.hostname, '-m', '0640', '-o',
|
||||
+ 'root:cockpit-ws', '-O', 'root:root', '-M',
|
||||
+ '0644'], raiseonerr=False)
|
||||
+
|
||||
+ master.run_command(['ln', '-s', paths.HTTP_KEYTAB, cls.symlink],
|
||||
+ raiseonerr=False)
|
||||
+
|
||||
+ time.sleep(5)
|
||||
+ master.run_command(['systemctl', 'enable', '--now', 'cockpit.socket'])
|
||||
+
|
||||
+ def test_login_with_kerberos(self):
|
||||
+ """
|
||||
+ Login to Cockpit using GSSAPI authentication
|
||||
+ """
|
||||
+ master = self.master
|
||||
+ tasks.kinit_admin(master)
|
||||
+
|
||||
+ cockpit_login = f'https://{master.hostname}:9090/cockpit/login'
|
||||
+ result = master.run_command([paths.BIN_CURL, '-u:', '--negotiate',
|
||||
+ '--cacert', paths.IPA_CA_CRT,
|
||||
+ cockpit_login])
|
||||
+ assert ("csrf-token" in result.stdout_text)
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -0,0 +1,400 @@
|
||||
From ffae7d109b9cc968bb9942da78ea4238eee5497b Mon Sep 17 00:00:00 2001
|
||||
From: Jay Gondaliya <jgondali@redhat.com>
|
||||
Date: Thu, 5 Mar 2026 19:07:06 +0530
|
||||
Subject: [PATCH] ipatests: Add ipa-selfservice users tests to xmlrpc
|
||||
|
||||
Convert self-service users tests from bash to Python and add them to the existing selfservice test file.
|
||||
|
||||
Tests verify that users can modify their own allowed attributes under default and custom selfservice rules,
|
||||
that disallowed attributes are rejected with ACIError, and that cross-user modification is blocked.
|
||||
Also covers atomic failure on mixed permissions, self-password-change, and user-find by phone, fax, and manager
|
||||
(BZ 1188195, 781208, 985016, 967509, 985013).
|
||||
|
||||
Signed-off-by: Jay Gondaliya jgondali@redhat.com
|
||||
Fixes: https://pagure.io/freeipa/issue/9945
|
||||
Assisted-by: Claude noreply@anthropic.com
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: PRANAV THUBE <pthube@redhat.com>
|
||||
---
|
||||
.../test_xmlrpc/test_selfservice_plugin.py | 360 +++++++++++++++++-
|
||||
1 file changed, 358 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_xmlrpc/test_selfservice_plugin.py b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
index 4c5059519..e55502a2d 100644
|
||||
--- a/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
@@ -21,8 +21,12 @@
|
||||
Test the `ipaserver/plugins/selfservice.py` module.
|
||||
"""
|
||||
|
||||
-from ipalib import errors
|
||||
-from ipatests.test_xmlrpc.xmlrpc_test import Declarative
|
||||
+from ipalib import api, errors
|
||||
+from ipatests.test_xmlrpc.xmlrpc_test import (
|
||||
+ Declarative, XMLRPC_test, assert_attr_equal,
|
||||
+)
|
||||
+from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker
|
||||
+from ipatests.util import change_principal, unlock_principal_password
|
||||
import pytest
|
||||
|
||||
selfservice1 = u'testself'
|
||||
@@ -394,3 +398,355 @@ class test_selfservice_misc(Declarative):
|
||||
),
|
||||
),
|
||||
]
|
||||
+
|
||||
+
|
||||
+SS_USER1 = 'ssuser0001'
|
||||
+SS_USER1_PASSWORD = 'Passw0rd1'
|
||||
+SS_USER2 = 'ssuser0002'
|
||||
+SS_USER2_PASSWORD = 'Passw0rd2'
|
||||
+SS_GOOD_MANAGER = 'ss_good_manager'
|
||||
+SS_GOOD_MANAGER_PASSWORD = 'Passw0rd3'
|
||||
+
|
||||
+SS_DEFAULT_SELFSERVICE = 'User Self service'
|
||||
+SS_CUSTOM_RULE = 'ss_test_rule0001'
|
||||
+
|
||||
+SS_DEFAULT_SELFSERVICE_ATTRS = [
|
||||
+ 'givenname', 'sn', 'cn', 'displayname', 'title', 'initials',
|
||||
+ 'loginshell', 'gecos', 'homephone', 'mobile', 'pager',
|
||||
+ 'facsimiletelephonenumber', 'telephonenumber', 'street',
|
||||
+ 'roomnumber', 'l', 'st', 'postalcode', 'manager', 'secretary',
|
||||
+ 'description', 'carlicense', 'labeleduri', 'inetuserhttpurl',
|
||||
+ 'seealso', 'employeetype', 'businesscategory', 'ou',
|
||||
+]
|
||||
+
|
||||
+SS_CUSTOM_RULE_ATTRS = [
|
||||
+ 'mobile', 'pager',
|
||||
+ 'facsimiletelephonenumber', 'telephonenumber',
|
||||
+]
|
||||
+
|
||||
+
|
||||
+def _safe_del_selfservice(name):
|
||||
+ """Delete a selfservice rule, ignoring NotFound."""
|
||||
+ try:
|
||||
+ api.Command['selfservice_del'](name)
|
||||
+ except errors.NotFound:
|
||||
+ pass
|
||||
+
|
||||
+
|
||||
+@pytest.fixture
|
||||
+def custom_selfservice_rule(xmlrpc_setup):
|
||||
+ """Replace the default selfservice rule with the narrow custom rule."""
|
||||
+ api.Command['selfservice_del'](SS_DEFAULT_SELFSERVICE)
|
||||
+ api.Command['selfservice_add'](
|
||||
+ SS_CUSTOM_RULE, attrs=SS_CUSTOM_RULE_ATTRS,
|
||||
+ )
|
||||
+ yield
|
||||
+ _safe_del_selfservice(SS_CUSTOM_RULE)
|
||||
+ api.Command['selfservice_add'](
|
||||
+ SS_DEFAULT_SELFSERVICE, attrs=SS_DEFAULT_SELFSERVICE_ATTRS,
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope='class')
|
||||
+def ss_user1(request, xmlrpc_setup):
|
||||
+ tracker = UserTracker(
|
||||
+ name=SS_USER1, givenname='Test', sn='User0001',
|
||||
+ userpassword=SS_USER1_PASSWORD,
|
||||
+ )
|
||||
+ tracker.make_fixture(request)
|
||||
+ tracker.make_create_command()()
|
||||
+ tracker.exists = True
|
||||
+ unlock_principal_password(
|
||||
+ SS_USER1, SS_USER1_PASSWORD, SS_USER1_PASSWORD,
|
||||
+ )
|
||||
+ return tracker
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope='class')
|
||||
+def ss_user2(request, xmlrpc_setup):
|
||||
+ tracker = UserTracker(
|
||||
+ name=SS_USER2, givenname='Test', sn='User0002',
|
||||
+ userpassword=SS_USER2_PASSWORD,
|
||||
+ )
|
||||
+ tracker.make_fixture(request)
|
||||
+ tracker.make_create_command()()
|
||||
+ tracker.exists = True
|
||||
+ unlock_principal_password(
|
||||
+ SS_USER2, SS_USER2_PASSWORD, SS_USER2_PASSWORD,
|
||||
+ )
|
||||
+ return tracker
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope='class')
|
||||
+def ss_good_manager(request, xmlrpc_setup):
|
||||
+ tracker = UserTracker(
|
||||
+ name=SS_GOOD_MANAGER, givenname='Good', sn='Manager',
|
||||
+ userpassword=SS_GOOD_MANAGER_PASSWORD,
|
||||
+ )
|
||||
+ tracker.make_fixture(request)
|
||||
+ tracker.make_create_command()()
|
||||
+ tracker.exists = True
|
||||
+ unlock_principal_password(
|
||||
+ SS_GOOD_MANAGER, SS_GOOD_MANAGER_PASSWORD, SS_GOOD_MANAGER_PASSWORD,
|
||||
+ )
|
||||
+ return tracker
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+@pytest.mark.usefixtures('ss_user1', 'ss_user2', 'ss_good_manager')
|
||||
+class test_selfservice_users(XMLRPC_test):
|
||||
+ """Test self-service user attribute modification permissions."""
|
||||
+
|
||||
+ # usertest_1001: Set all attrs allowed by default self-service rule.
|
||||
+ def test_set_all_default_selfservice_attrs(self):
|
||||
+ """Set all attrs allowed by the default self-service rule."""
|
||||
+ attrs = {
|
||||
+ 'givenname': 'Good',
|
||||
+ 'sn': 'User',
|
||||
+ 'cn': 'gooduser',
|
||||
+ 'displayname': 'gooduser',
|
||||
+ 'initials': 'GU',
|
||||
+ 'gecos': 'gooduser@good.example.com',
|
||||
+ 'loginshell': '/bin/bash',
|
||||
+ 'street': 'Good_Street_Rd',
|
||||
+ 'l': 'Good_City',
|
||||
+ 'st': 'Goodstate',
|
||||
+ 'postalcode': '33333',
|
||||
+ 'telephonenumber': '333-333-3333',
|
||||
+ 'mobile': '333-333-3333',
|
||||
+ 'pager': '333-333-3333',
|
||||
+ 'facsimiletelephonenumber': '333-333-3333',
|
||||
+ 'ou': 'good-org',
|
||||
+ 'title': 'good_admin',
|
||||
+ 'manager': SS_GOOD_MANAGER,
|
||||
+ 'carlicense': 'good-3333',
|
||||
+ }
|
||||
+
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ for attr, value in attrs.items():
|
||||
+ api.Command['user_mod'](SS_USER1, **{attr: value})
|
||||
+
|
||||
+ entry = api.Command['user_show'](SS_USER1, all=True)['result']
|
||||
+ for attr, value in attrs.items():
|
||||
+ assert_attr_equal(entry, attr, value)
|
||||
+
|
||||
+ # usertest_1002: Test that default disallowed attributes are rejected.
|
||||
+ def test_reject_uidnumber_by_default(self):
|
||||
+ """uidnumber change is rejected by default."""
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ api.Command['user_mod'](SS_USER1, uidnumber=9999)
|
||||
+
|
||||
+ def test_reject_gidnumber_by_default(self):
|
||||
+ """gidnumber change is rejected by default."""
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ api.Command['user_mod'](SS_USER1, gidnumber=9999)
|
||||
+
|
||||
+ def test_reject_homedirectory_by_default(self):
|
||||
+ """homedirectory change is rejected by default."""
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ api.Command['user_mod'](
|
||||
+ SS_USER1, homedirectory='/home/gooduser')
|
||||
+
|
||||
+ def test_reject_email_by_default(self):
|
||||
+ """email change is rejected by default."""
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ api.Command['user_mod'](
|
||||
+ SS_USER1, mail='gooduser@good.example.com')
|
||||
+
|
||||
+ # usertest_1003: All attrs rejected when the default rule is deleted.
|
||||
+ def test_all_attrs_rejected_without_default_rule(self):
|
||||
+ """All attrs are rejected when the default rule is deleted."""
|
||||
+ attrs = {
|
||||
+ 'givenname': 'Bad',
|
||||
+ 'sn': 'LUser',
|
||||
+ 'cn': 'badluser',
|
||||
+ 'displayname': 'badluser',
|
||||
+ 'initials': 'BL',
|
||||
+ 'gecos': 'badluser@bad.example.com',
|
||||
+ 'loginshell': '/bin/tcsh',
|
||||
+ 'street': 'Bad_Street_Av',
|
||||
+ 'l': 'Bad_City',
|
||||
+ 'st': 'Badstate',
|
||||
+ 'postalcode': '99999',
|
||||
+ 'telephonenumber': '999-999-9999',
|
||||
+ 'mobile': '999-999-9999',
|
||||
+ 'pager': '999-999-9999',
|
||||
+ 'facsimiletelephonenumber': '999-999-9999',
|
||||
+ 'ou': 'bad-org',
|
||||
+ 'title': 'bad_admin',
|
||||
+ 'manager': 'admin',
|
||||
+ 'carlicense': 'bad-9999',
|
||||
+ }
|
||||
+
|
||||
+ api.Command['selfservice_del'](SS_DEFAULT_SELFSERVICE)
|
||||
+ try:
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ for attr, value in attrs.items():
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ api.Command['user_mod'](SS_USER1, **{attr: value})
|
||||
+ finally:
|
||||
+ api.Command['selfservice_add'](
|
||||
+ SS_DEFAULT_SELFSERVICE,
|
||||
+ attrs=SS_DEFAULT_SELFSERVICE_ATTRS,
|
||||
+ )
|
||||
+
|
||||
+ # usertest_1004: Custom rule grants write access to its specified attrs.
|
||||
+ def test_custom_rule_grants_write_access(
|
||||
+ self, custom_selfservice_rule):
|
||||
+ """Custom rule grants write access to its specified attrs."""
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ api.Command['user_mod'](
|
||||
+ SS_USER1, telephonenumber='777-777-7777')
|
||||
+ api.Command['user_mod'](SS_USER1, mobile='777-777-7777')
|
||||
+ api.Command['user_mod'](SS_USER1, pager='777-777-7777')
|
||||
+ api.Command['user_mod'](
|
||||
+ SS_USER1,
|
||||
+ facsimiletelephonenumber='777-777-7777')
|
||||
+
|
||||
+ # usertest_1005: Persisted attrs and user-find by phone, fax, manager.
|
||||
+ def test_verify_persisted_attrs(self):
|
||||
+ """Verify attrs set by previous tests are persisted."""
|
||||
+ expected = {
|
||||
+ 'givenname': 'Good',
|
||||
+ 'sn': 'User',
|
||||
+ 'cn': 'gooduser',
|
||||
+ 'displayname': 'gooduser',
|
||||
+ 'initials': 'GU',
|
||||
+ 'gecos': 'gooduser@good.example.com',
|
||||
+ 'loginshell': '/bin/bash',
|
||||
+ 'street': 'Good_Street_Rd',
|
||||
+ 'l': 'Good_City',
|
||||
+ 'st': 'Goodstate',
|
||||
+ 'postalcode': '33333',
|
||||
+ 'telephonenumber': '777-777-7777',
|
||||
+ 'mobile': '777-777-7777',
|
||||
+ 'pager': '777-777-7777',
|
||||
+ 'facsimiletelephonenumber': '777-777-7777',
|
||||
+ 'ou': 'good-org',
|
||||
+ 'title': 'good_admin',
|
||||
+ 'carlicense': 'good-3333',
|
||||
+ }
|
||||
+
|
||||
+ entry = api.Command['user_show'](SS_USER1, all=True)['result']
|
||||
+ for attr, value in expected.items():
|
||||
+ assert_attr_equal(entry, attr, value)
|
||||
+ assert_attr_equal(entry, 'manager', SS_GOOD_MANAGER)
|
||||
+
|
||||
+ def test_user_find_by_phone(self):
|
||||
+ """BZ 1188195: user-find by phone number returns results."""
|
||||
+ result = api.Command['user_find'](
|
||||
+ telephonenumber='777-777-7777')
|
||||
+ assert result['count'] >= 1
|
||||
+ uids = [e['uid'][0] for e in result['result']]
|
||||
+ assert SS_USER1 in uids
|
||||
+
|
||||
+ def test_user_find_by_fax(self):
|
||||
+ """BZ 1188195: user-find by fax number returns results."""
|
||||
+ result = api.Command['user_find'](
|
||||
+ facsimiletelephonenumber='777-777-7777')
|
||||
+ assert result['count'] >= 1
|
||||
+ uids = [e['uid'][0] for e in result['result']]
|
||||
+ assert SS_USER1 in uids
|
||||
+
|
||||
+ def test_user_find_by_manager(self):
|
||||
+ """BZ 781208: user-find by manager returns matches."""
|
||||
+ result = api.Command['user_find'](
|
||||
+ SS_USER1, manager=SS_GOOD_MANAGER)
|
||||
+ assert result['count'] >= 1, (
|
||||
+ 'BZ 781208: user-find --manager did not find matches'
|
||||
+ )
|
||||
+ uids = [e['uid'][0] for e in result['result']]
|
||||
+ assert SS_USER1 in uids
|
||||
+
|
||||
+ # usertest_1006: BZ 985016, 967509: user can modify an allowed attr.
|
||||
+ def test_user_can_modify_allowed_attr(self):
|
||||
+ """BZ 985016, 967509: user can modify an allowed attr."""
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ api.Command['user_mod'](SS_USER1, mobile='888-888-8888')
|
||||
+ entry = api.Command['user_show'](SS_USER1, all=True)['result']
|
||||
+ assert_attr_equal(entry, 'mobile', '888-888-8888')
|
||||
+
|
||||
+ # usertest_1007: BZ 985016, 967509: disallowed attribute is rejected.
|
||||
+ def test_disallowed_attr_rejected_with_custom_rule(
|
||||
+ self, custom_selfservice_rule):
|
||||
+ """BZ 985016, 967509: disallowed attribute is rejected."""
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ api.Command['user_mod'](SS_USER1, title='Dr')
|
||||
+
|
||||
+ # usertest_1008: user-mod fails atomically on mixed attr permissions.
|
||||
+ def test_user_mod_atomic_failure_mixed_perms(
|
||||
+ self, custom_selfservice_rule):
|
||||
+ """user-mod fails atomically when one attr is disallowed."""
|
||||
+ original_title = api.Command['user_show'](
|
||||
+ SS_USER1)['result'].get('title')
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ api.Command['user_mod'](
|
||||
+ SS_USER1,
|
||||
+ title='notgonnawork',
|
||||
+ telephonenumber='999-999-9990',
|
||||
+ )
|
||||
+ result = api.Command['user_find'](
|
||||
+ SS_USER1, telephonenumber='999-999-9990')
|
||||
+ assert result['count'] == 0, (
|
||||
+ 'Phone was changed despite disallowed title in same call'
|
||||
+ )
|
||||
+ after = api.Command['user_show'](SS_USER1)['result']
|
||||
+ assert after.get('title') == original_title, (
|
||||
+ 'Title was modified despite being disallowed'
|
||||
+ )
|
||||
+
|
||||
+ # usertest_1009: BZ 985013: user can change their own password.
|
||||
+ def test_self_password_change_via_passwd(self):
|
||||
+ """BZ 985013: user can change their own password via passwd."""
|
||||
+ policy = api.Command['pwpolicy_show']()['result']
|
||||
+ orig_minlife = policy.get('krbminpwdlife', ('1',))[0]
|
||||
+
|
||||
+ api.Command['pwpolicy_mod'](krbminpwdlife=0)
|
||||
+ try:
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ api.Command['passwd'](
|
||||
+ SS_USER1,
|
||||
+ password='MyN3wP@55',
|
||||
+ current_password=SS_USER1_PASSWORD,
|
||||
+ )
|
||||
+ # Reset password so the next test can authenticate
|
||||
+ unlock_principal_password(
|
||||
+ SS_USER1, 'MyN3wP@55', SS_USER1_PASSWORD,
|
||||
+ )
|
||||
+ finally:
|
||||
+ api.Command['pwpolicy_mod'](krbminpwdlife=int(orig_minlife))
|
||||
+
|
||||
+ def test_self_password_change_via_user_mod(self):
|
||||
+ """BZ 985013: user can change their own password via user_mod."""
|
||||
+ policy = api.Command['pwpolicy_show']()['result']
|
||||
+ orig_minlife = policy.get('krbminpwdlife', ('1',))[0]
|
||||
+
|
||||
+ api.Command['pwpolicy_mod'](krbminpwdlife=0)
|
||||
+ try:
|
||||
+ with change_principal(SS_USER1, SS_USER1_PASSWORD):
|
||||
+ api.Command['user_mod'](
|
||||
+ SS_USER1,
|
||||
+ userpassword='MyN3wP@55',
|
||||
+ )
|
||||
+ finally:
|
||||
+ api.Command['pwpolicy_mod'](krbminpwdlife=int(orig_minlife))
|
||||
+
|
||||
+ # usertest_1010: User cannot modify another user's attributes.
|
||||
+ def test_cross_user_modification_rejected(self):
|
||||
+ """User cannot modify another user's attributes."""
|
||||
+ with change_principal(SS_USER2, SS_USER2_PASSWORD):
|
||||
+ with pytest.raises(errors.ACIError):
|
||||
+ api.Command['user_mod'](SS_USER1, mobile='867-5309')
|
||||
+
|
||||
+ def test_verify_cross_user_modification_rejected(self):
|
||||
+ """Verify attrs did not change after cross-user modification."""
|
||||
+ result = api.Command['user_find'](SS_USER1, mobile='867-5309')
|
||||
+ assert result['count'] == 0, (
|
||||
+ 'Mobile was changed by a different user'
|
||||
+ )
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
From c306c613399cdd9a2c716b83ce0d47d320aec2a8 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Tue, 19 Nov 2024 12:57:46 +0200
|
||||
Subject: [PATCH] ipaserver/dcerpc: support Samba 4.21
|
||||
|
||||
Samba 4.21 moved samba.trust_utils module to samba.lsa_utils.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9702
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipaserver/dcerpc.py | 8 ++++++--
|
||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
|
||||
index a28c72361276f12a1a02cd126425ac3c62eddd4f..3344ea226e3cba61912e717f9c375612bb4707e0 100644
|
||||
--- a/ipaserver/dcerpc.py
|
||||
+++ b/ipaserver/dcerpc.py
|
||||
@@ -55,9 +55,13 @@ from samba import ntstatus
|
||||
import samba
|
||||
|
||||
try:
|
||||
- from samba.trust_utils import CreateTrustedDomainRelax
|
||||
+ from samba.lsa_utils import CreateTrustedDomainRelax
|
||||
except ImportError:
|
||||
- CreateTrustedDomainRelax = None
|
||||
+ try:
|
||||
+ from samba.trust_utils import CreateTrustedDomainRelax
|
||||
+ except ImportError:
|
||||
+ CreateTrustedDomainRelax = None
|
||||
+
|
||||
try:
|
||||
from samba import arcfour_encrypt
|
||||
except ImportError:
|
||||
--
|
||||
2.47.0
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
From 7de7c0e2ed1afb2887bc7adeb7363109cbc5f3f9 Mon Sep 17 00:00:00 2001
|
||||
From 313bd8ff118a79dca5aad0b19ec8f69519258f89 Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Wed, 11 Mar 2026 20:13:31 +0530
|
||||
Subject: [PATCH] ipatests: Fix test_allow_query_transfer_ipv6 when IPv6 is
|
||||
disabled
|
||||
Subject: [PATCH] ipatests: Fix test_allow_query_transfer_ipv6 when
|
||||
IPv6 is disabled
|
||||
|
||||
The test was failing in environments where IPv6 is disabled at the
|
||||
kernel level because it attempted to add a temporary IPv6 address
|
||||
@ -25,7 +25,7 @@ Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
1 file changed, 14 insertions(+), 48 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_dns.py b/ipatests/test_integration/test_dns.py
|
||||
index 4b9ab1fe8d7b8884760ed637cb2fcc5d5a060df0..947cff5c02ea2662cc2de88860cfa294e396792d 100644
|
||||
index 4b9ab1fe8..947cff5c0 100644
|
||||
--- a/ipatests/test_integration/test_dns.py
|
||||
+++ b/ipatests/test_integration/test_dns.py
|
||||
@@ -5,6 +5,7 @@
|
||||
@ -1,66 +0,0 @@
|
||||
From 184589fac4ff36b5583541f40dff91296c33370a Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Mon, 2 Dec 2024 10:23:29 -0500
|
||||
Subject: [PATCH] Allow looking up constants.Group by gid in addition to name
|
||||
|
||||
This adds flexibility so we can look up groups by both gid and
|
||||
by name in order to have a more consistent API for management.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9709
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
---
|
||||
ipaplatform/base/constants.py | 5 ++++-
|
||||
ipatests/test_ipaplatform/test_constants.py | 11 +++++++++++
|
||||
2 files changed, 15 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py
|
||||
index 1689efe52466f00fd8b014f720e1d21ebdbf2504..f1ef7efff502573bab82e890bcdf87c0ec52a399 100644
|
||||
--- a/ipaplatform/base/constants.py
|
||||
+++ b/ipaplatform/base/constants.py
|
||||
@@ -86,7 +86,10 @@ class Group(_Entity):
|
||||
try:
|
||||
self._entity = entity = grp.getgrnam(self)
|
||||
except KeyError:
|
||||
- raise ValueError(f"group '{self!s}' not found") from None
|
||||
+ try:
|
||||
+ self._entity = entity = grp.getgrgid(int(self))
|
||||
+ except (TypeError, ValueError):
|
||||
+ raise ValueError(f"group '{self!s}' not found") from None
|
||||
return entity
|
||||
|
||||
@property
|
||||
diff --git a/ipatests/test_ipaplatform/test_constants.py b/ipatests/test_ipaplatform/test_constants.py
|
||||
index b57bfa48e5ccefe2b22cb00aca8436e0edc01a30..9bb12283609f87bcd875a2c55ee1e8b714dd8b3a 100644
|
||||
--- a/ipatests/test_ipaplatform/test_constants.py
|
||||
+++ b/ipatests/test_ipaplatform/test_constants.py
|
||||
@@ -1,6 +1,7 @@
|
||||
#
|
||||
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
+import grp
|
||||
import pytest
|
||||
|
||||
from ipaplatform.base.constants import User, Group
|
||||
@@ -40,6 +41,16 @@ def test_group():
|
||||
assert Group(str(group)) is not group
|
||||
|
||||
|
||||
+def test_numeric_group():
|
||||
+ g = grp.getgrnam('apache')
|
||||
+ group = Group(g.gr_gid)
|
||||
+ assert group.gid == g.gr_gid
|
||||
+ assert type(str(group)) is str
|
||||
+ assert repr(group) == '<Group "%d">' % g.gr_gid
|
||||
+ assert group.gid == g.gr_gid
|
||||
+ assert group.entity.gr_gid == g.gr_gid
|
||||
+
|
||||
+
|
||||
def test_group_invalid():
|
||||
invalid = Group("invalid")
|
||||
with pytest.raises(ValueError) as e:
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -0,0 +1,217 @@
|
||||
From c8a832e19699a7bb6ff486055015f033a3137e5f Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Mon, 16 Mar 2026 14:29:12 +0530
|
||||
Subject: [PATCH] ipatests: Add XML-RPC tests for i18n user attributes
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Add tests for internationalization support in user plugin:
|
||||
- User creation/deletion with i18n givenname and sn
|
||||
- Lastname modification with Swedish/European names (13 values)
|
||||
- Firstname modification with European accented names (4 values)
|
||||
- Firstname modification with single i18n characters (67 values)
|
||||
|
||||
Test data includes characters like Çándide, Örjan, Éric, ß, ü, etc.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9959
|
||||
Signed-off-by: Pranav Thube pthube@redhat.com
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Carla Martinez <carlmart@redhat.com>
|
||||
---
|
||||
ipatests/test_xmlrpc/test_i18n_user_plugin.py | 182 ++++++++++++++++++
|
||||
1 file changed, 182 insertions(+)
|
||||
create mode 100644 ipatests/test_xmlrpc/test_i18n_user_plugin.py
|
||||
|
||||
diff --git a/ipatests/test_xmlrpc/test_i18n_user_plugin.py b/ipatests/test_xmlrpc/test_i18n_user_plugin.py
|
||||
new file mode 100644
|
||||
index 000000000..146ffdc51
|
||||
--- /dev/null
|
||||
+++ b/ipatests/test_xmlrpc/test_i18n_user_plugin.py
|
||||
@@ -0,0 +1,182 @@
|
||||
+# Authors:
|
||||
+# Pranav Thube <pthube@redhat.com>
|
||||
+#
|
||||
+# Copyright (C) 2026 Red Hat
|
||||
+# see file 'COPYING' for use and warranty information
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify
|
||||
+# it under the terms of the GNU General Public License as published by
|
||||
+# the Free Software Foundation, either version 3 of the License, or
|
||||
+# (at your option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful,
|
||||
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+# GNU General Public License for more details.
|
||||
+#
|
||||
+# You should have received a copy of the GNU General Public License
|
||||
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
+
|
||||
+"""
|
||||
+Test the i18n (internationalization) support for user plugin.
|
||||
+
|
||||
+This module tests that IPA correctly handles international characters
|
||||
+in user attributes such as first name (givenname) and last name (sn).
|
||||
+"""
|
||||
+
|
||||
+import pytest
|
||||
+
|
||||
+from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
|
||||
+from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker
|
||||
+
|
||||
+
|
||||
+# Test data Users with i18n names
|
||||
+I18N_USERS = {
|
||||
+ 'user1': {
|
||||
+ 'name': 'i18nuser1',
|
||||
+ 'givenname': 'Çándide',
|
||||
+ 'sn': 'Rùiz',
|
||||
+ },
|
||||
+ 'user2': {
|
||||
+ 'name': 'i18nuser2',
|
||||
+ 'givenname': 'Rôséñe',
|
||||
+ 'sn': 'zackr',
|
||||
+ },
|
||||
+ 'user3': {
|
||||
+ 'name': 'i18nuser3',
|
||||
+ 'givenname': 'Älka',
|
||||
+ 'sn': 'Màrzella',
|
||||
+ },
|
||||
+ 'user4': {
|
||||
+ 'name': 'i18nuser4',
|
||||
+ 'givenname': 'Feâtlëss',
|
||||
+ 'sn': 'Watérmân',
|
||||
+ },
|
||||
+}
|
||||
+
|
||||
+# CNS test data - Swedish/European last names
|
||||
+CNS_LASTNAMES = [
|
||||
+ 'Oskar',
|
||||
+ 'Anders',
|
||||
+ 'Örjan',
|
||||
+ 'Jonas',
|
||||
+ 'Ulf',
|
||||
+ 'Äke',
|
||||
+ 'Bertold',
|
||||
+ 'Bruno',
|
||||
+ 'Didier',
|
||||
+ 'Éric',
|
||||
+ 'Jean-Luc',
|
||||
+ 'Laurent',
|
||||
+ 'Têko',
|
||||
+]
|
||||
+
|
||||
+# European names with mixed accents for firstname tests
|
||||
+EUROPEAN_FIRSTNAMES = [
|
||||
+ 'Rôséñel',
|
||||
+ 'Tàrqùinio',
|
||||
+ 'PASSWÖRD',
|
||||
+ 'Nomeuropéen',
|
||||
+ # Names with special characters (apostrophe, space)
|
||||
+ "O'Brian",
|
||||
+ 'Maria José',
|
||||
+]
|
||||
+
|
||||
+# Firstname test data - Single characters including accented
|
||||
+# 73 characters total: 26 ASCII A-Z + 47 accented/special characters
|
||||
+FIRSTNAME_SINGLE_CHARS = [
|
||||
+ # ASCII uppercase letters A-Z (26 characters)
|
||||
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
||||
+ 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
+ # Extended Latin uppercase characters (20 characters)
|
||||
+ 'À', 'Á', 'Â', 'Ä', 'Ç', 'È', 'É', 'Ê', 'Ë',
|
||||
+ 'Í', 'Î', 'Ï', 'Ñ', 'Ó', 'Ô', 'Ö', 'Ù', 'Ú', 'Û', 'Ü',
|
||||
+ # German eszett (1 character)
|
||||
+ 'ß',
|
||||
+ # Extended Latin lowercase characters (20 characters)
|
||||
+ 'à', 'á', 'â', 'ä', 'ç', 'è', 'é', 'ê', 'ë',
|
||||
+ 'í', 'î', 'ï', 'ñ', 'ó', 'ô', 'ö', 'ù', 'ú', 'û', 'ü',
|
||||
+ # Nordic characters (4 characters)
|
||||
+ 'Ø', 'ø', 'Å', 'å',
|
||||
+ # Polish character (2 characters)
|
||||
+ 'Ł', 'ł',
|
||||
+]
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope='class')
|
||||
+def i18n_users(request, xmlrpc_setup):
|
||||
+ """Single fixture providing all i18n test users as a dictionary"""
|
||||
+ users = {}
|
||||
+ for user_key, user_data in I18N_USERS.items():
|
||||
+ tracker = UserTracker(
|
||||
+ name=user_data['name'],
|
||||
+ givenname=user_data['givenname'],
|
||||
+ sn=user_data['sn']
|
||||
+ )
|
||||
+ users[user_key] = tracker.make_fixture(request)
|
||||
+ return users
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+class TestI18nUser(XMLRPC_test):
|
||||
+ """
|
||||
+ Test i18n (internationalization) support for user plugin.
|
||||
+
|
||||
+ Tests that IPA correctly handles international characters in user
|
||||
+ attributes such as first name (givenname) and last name (sn).
|
||||
+ """
|
||||
+
|
||||
+ ##########################################################################
|
||||
+ # User Creation Tests
|
||||
+ ##########################################################################
|
||||
+
|
||||
+ @pytest.mark.parametrize('user_key', I18N_USERS.keys())
|
||||
+ def test_add_i18n_user(self, i18n_users, user_key):
|
||||
+ """Adding i18n user"""
|
||||
+ i18n_users[user_key].create()
|
||||
+
|
||||
+ @pytest.mark.parametrize('user_key', I18N_USERS.keys())
|
||||
+ def test_verify_i18n_user(self, i18n_users, user_key):
|
||||
+ """Verify i18n user has correct full name"""
|
||||
+ user = i18n_users[user_key]
|
||||
+ user.ensure_exists()
|
||||
+ command = user.make_find_command(uid=user.uid, all=True)
|
||||
+ result = command()
|
||||
+ assert result['count'] == 1
|
||||
+ entry = result['result'][0]
|
||||
+ assert I18N_USERS[user_key]['givenname'] in entry['givenname']
|
||||
+ assert I18N_USERS[user_key]['sn'] in entry['sn']
|
||||
+
|
||||
+ ##########################################################################
|
||||
+ # CNS Tests - Lastname modification with Swedish/European names
|
||||
+ ##########################################################################
|
||||
+
|
||||
+ @pytest.mark.parametrize('lastname', CNS_LASTNAMES)
|
||||
+ def test_cns_modify_lastname(self, i18n_users, lastname):
|
||||
+ """Modify lastname to Swedish/European name"""
|
||||
+ user = i18n_users['user1']
|
||||
+ user.ensure_exists()
|
||||
+ user.update(dict(sn=lastname))
|
||||
+
|
||||
+ ##########################################################################
|
||||
+ # European accented firstname tests
|
||||
+ ##########################################################################
|
||||
+
|
||||
+ @pytest.mark.parametrize('firstname', EUROPEAN_FIRSTNAMES)
|
||||
+ def test_european_modify_firstname(self, i18n_users, firstname):
|
||||
+ """Modify firstname to European accented name"""
|
||||
+ user = i18n_users['user2']
|
||||
+ user.ensure_exists()
|
||||
+ user.update(dict(givenname=firstname))
|
||||
+
|
||||
+ ##########################################################################
|
||||
+ # Firstname Tests - Single character modification
|
||||
+ ##########################################################################
|
||||
+
|
||||
+ @pytest.mark.parametrize('char', FIRSTNAME_SINGLE_CHARS)
|
||||
+ def test_firstname_modify_single_char(self, i18n_users, char):
|
||||
+ """Modify firstname to single character"""
|
||||
+ user = i18n_users['user3']
|
||||
+ user.ensure_exists()
|
||||
+ user.update(dict(givenname=char))
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
From 934d4a291d44a40b5ea006aa1f09afa8e4a985fc Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Mon, 2 Dec 2024 10:27:15 -0500
|
||||
Subject: [PATCH] Pass all pkiuser groups as suplementary when validating an
|
||||
HSM
|
||||
|
||||
We were doing a "best effort" when validating the HSM token is
|
||||
visible with a valid PIN when it came to groups. A specific
|
||||
workaround was added for softhsm2 but this didn't carry over
|
||||
to other HSMs that may have group-specific read/write access.
|
||||
|
||||
Use the new capability in ipaplatform.constants.py::Group to be
|
||||
able to use generate a valid entry from a group GID. Pair this
|
||||
with os.getgrouplist() and all groups will be passed correctly
|
||||
via ipautil.run().
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9709
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
---
|
||||
ipaserver/install/ca.py | 12 ++++--------
|
||||
1 file changed, 4 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
|
||||
index 520e3fc5de1084e7c22c0cf7eaa86e1d3c421373..2959aceed5cd2fd4851457eaa4aeac3c0905d27d 100644
|
||||
--- a/ipaserver/install/ca.py
|
||||
+++ b/ipaserver/install/ca.py
|
||||
@@ -211,11 +211,7 @@ def hsm_validator(token_name, token_library, token_password):
|
||||
)
|
||||
pkiuser = constants.PKI_USER
|
||||
pkigroup = constants.PKI_GROUP
|
||||
- if 'libsofthsm' in token_library:
|
||||
- import grp
|
||||
- group = grp.getgrnam(constants.ODS_GROUP)
|
||||
- if str(constants.PKI_USER) in group.gr_mem:
|
||||
- pkigroup = constants.ODS_GROUP
|
||||
+ group_list = os.getgrouplist(pkiuser, pkigroup.gid)
|
||||
with certdb.NSSDatabase() as tempnssdb:
|
||||
tempnssdb.create_db(user=str(pkiuser), group=str(pkigroup))
|
||||
# Try adding the token library to the temporary database in
|
||||
@@ -231,7 +227,7 @@ def hsm_validator(token_name, token_library, token_password):
|
||||
# It may fail if p11-kit has already registered the library, that's
|
||||
# ok.
|
||||
ipautil.run(command, stdin='\n', cwd=tempnssdb.secdir,
|
||||
- runas=pkiuser, suplementary_groups=[pkigroup],
|
||||
+ runas=pkiuser, suplementary_groups=group_list,
|
||||
raiseonerr=False)
|
||||
|
||||
command = [
|
||||
@@ -242,7 +238,7 @@ def hsm_validator(token_name, token_library, token_password):
|
||||
]
|
||||
lines = ipautil.run(
|
||||
command, cwd=tempnssdb.secdir, capture_output=True,
|
||||
- runas=pkiuser, suplementary_groups=[pkigroup]).output
|
||||
+ runas=pkiuser, suplementary_groups=group_list).output
|
||||
found = False
|
||||
token_line = f'token: {token_name}'
|
||||
for line in lines.split('\n'):
|
||||
@@ -265,7 +261,7 @@ def hsm_validator(token_name, token_library, token_password):
|
||||
]
|
||||
result = ipautil.run(args, cwd=tempnssdb.secdir,
|
||||
runas=pkiuser,
|
||||
- suplementary_groups=[pkigroup],
|
||||
+ suplementary_groups=group_list,
|
||||
capture_error=True, raiseonerr=False)
|
||||
if result.returncode != 0 and len(result.error_output):
|
||||
if 'SEC_ERROR_BAD_PASSWORD' in result.error_output:
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -0,0 +1,229 @@
|
||||
From 3056bf8fb27732213591f4c86044ce6980054ec9 Mon Sep 17 00:00:00 2001
|
||||
From: Jay Gondaliya <jgondali@redhat.com>
|
||||
Date: Tue, 10 Mar 2026 19:14:48 +0530
|
||||
Subject: [PATCH] ipatests: Add selfservice-add and selfservice-del cli
|
||||
tests
|
||||
|
||||
Add a new Declarative test class `test_selfservice_cli_add_del` covering CLI-level behaviour of the selfservice-add and selfservice-del commands:
|
||||
|
||||
- add_1002: bad attrs with valid permissions rejects with InvalidSyntax
|
||||
- add_1003: valid attrs with invalid permissions rejects with ValidationError
|
||||
- add_1004: valid attrs and permissions with --all --raw succeeds and
|
||||
returns the raw ACI string (BZ 772106)
|
||||
- add_1005: bad attrs only rejects with InvalidSyntax
|
||||
- add_1006: valid attrs only succeeds with default write permission
|
||||
- del_1001: deleting an existing selfservice rule succeeds
|
||||
- del_1002: deleting a non-existent rule raises NotFound
|
||||
|
||||
Signed-off-by: Jay Gondaliya <jgondali@redhat.com>
|
||||
Fixes: https://pagure.io/freeipa/issue/9945
|
||||
Assisted-by: Claude noreply@anthropic.com
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
.../test_xmlrpc/test_selfservice_plugin.py | 192 ++++++++++++++++++
|
||||
1 file changed, 192 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_xmlrpc/test_selfservice_plugin.py b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
index e55502a2d..8f2307a20 100644
|
||||
--- a/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
@@ -750,3 +750,195 @@ class test_selfservice_users(XMLRPC_test):
|
||||
assert result['count'] == 0, (
|
||||
'Mobile was changed by a different user'
|
||||
)
|
||||
+
|
||||
+
|
||||
+# Module-level constants for CLI test classes
|
||||
+# selfservice-add / selfservice-del CLI tests
|
||||
+SS_CLI_ADD_1004 = 'selfservice_add_1004'
|
||||
+SS_CLI_ADD_1006 = 'selfservice_add_1006'
|
||||
+SS_CLI_DEL_1001 = 'selfservice_del_1001'
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+class test_selfservice_cli_add_del(Declarative):
|
||||
+ """CLI tests for selfservice-add and selfservice-del commands."""
|
||||
+
|
||||
+ cleanup_commands = [
|
||||
+ ('selfservice_del', [SS_CLI_ADD_1004], {}),
|
||||
+ ('selfservice_del', [SS_CLI_ADD_1006], {}),
|
||||
+ ]
|
||||
+
|
||||
+ tests = [
|
||||
+
|
||||
+ # add_1002: bad attrs + valid permissions + --all --raw
|
||||
+ dict(
|
||||
+ desc='add_1002: selfservice-add with bad attrs, valid permissions,'
|
||||
+ ' --all --raw',
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ ['selfservice_add_1002'],
|
||||
+ dict(
|
||||
+ attrs=['badattr'],
|
||||
+ permissions='write',
|
||||
+ all=True,
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=errors.InvalidSyntax(
|
||||
+ attr=r'targetattr "badattr" does not exist in schema. '
|
||||
+ r'Please add attributeTypes "badattr" to '
|
||||
+ r'schema if necessary. '
|
||||
+ r'ACL Syntax Error(-5):'
|
||||
+ r'(targetattr = \22badattr\22)'
|
||||
+ r'(version 3.0;acl '
|
||||
+ r'\22selfservice:selfservice_add_1002\22;'
|
||||
+ r'allow (write) userdn = \22ldap:///self\22;)',
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # add_1003: valid attrs + bad permissions + --all --raw
|
||||
+ dict(
|
||||
+ desc='add_1003: selfservice-add with valid attrs, bad permissions,'
|
||||
+ ' --all --raw',
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ ['selfservice_add_1003'],
|
||||
+ dict(
|
||||
+ attrs=[
|
||||
+ 'telephonenumber', 'mobile',
|
||||
+ 'pager', 'facsimiletelephonenumber',
|
||||
+ ],
|
||||
+ permissions='badperm',
|
||||
+ all=True,
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=errors.ValidationError(
|
||||
+ name='permissions',
|
||||
+ error='"badperm" is not a valid permission',
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # add_1004: valid attrs + valid permissions + --all --raw (BZ 772106)
|
||||
+ # selfservice-add with --raw must not return "internal error" message.
|
||||
+ dict(
|
||||
+ desc='add_1004: selfservice-add with valid attrs and permissions,'
|
||||
+ ' --all --raw (BZ 772106)',
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ [SS_CLI_ADD_1004],
|
||||
+ dict(
|
||||
+ attrs=[
|
||||
+ 'telephonenumber', 'mobile',
|
||||
+ 'pager', 'facsimiletelephonenumber',
|
||||
+ ],
|
||||
+ permissions='write',
|
||||
+ all=True,
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_ADD_1004,
|
||||
+ summary='Added selfservice "%s"' % SS_CLI_ADD_1004,
|
||||
+ result={
|
||||
+ 'aci': (
|
||||
+ '(targetattr = "telephonenumber || mobile || pager'
|
||||
+ ' || facsimiletelephonenumber")'
|
||||
+ '(version 3.0;acl "selfservice:%s";'
|
||||
+ 'allow (write) userdn = "ldap:///self";)'
|
||||
+ % SS_CLI_ADD_1004
|
||||
+ ),
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # add_1005: bad attrs only
|
||||
+ dict(
|
||||
+ desc='add_1005: selfservice-add with bad attrs only',
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ ['selfservice_add_1005'],
|
||||
+ dict(attrs=['badattrs']),
|
||||
+ ),
|
||||
+ expected=errors.InvalidSyntax(
|
||||
+ attr=r'targetattr "badattrs" does not exist in schema. '
|
||||
+ r'Please add attributeTypes "badattrs" to '
|
||||
+ r'schema if necessary. '
|
||||
+ r'ACL Syntax Error(-5):'
|
||||
+ r'(targetattr = \22badattrs\22)'
|
||||
+ r'(version 3.0;acl '
|
||||
+ r'\22selfservice:selfservice_add_1005\22;'
|
||||
+ r'allow (write) userdn = \22ldap:///self\22;)',
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # add_1006: valid attrs only
|
||||
+ dict(
|
||||
+ desc='add_1006: selfservice-add with valid attrs only',
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ [SS_CLI_ADD_1006],
|
||||
+ dict(attrs=[
|
||||
+ 'telephonenumber', 'mobile',
|
||||
+ 'pager', 'facsimiletelephonenumber',
|
||||
+ ]),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_ADD_1006,
|
||||
+ summary='Added selfservice "%s"' % SS_CLI_ADD_1006,
|
||||
+ result=dict(
|
||||
+ attrs=[
|
||||
+ 'telephonenumber', 'mobile',
|
||||
+ 'pager', 'facsimiletelephonenumber',
|
||||
+ ],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_ADD_1006,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Setup for del tests: create the rule that del_1001 will delete.
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Setup: create %r for selfservice-del tests'
|
||||
+ % SS_CLI_DEL_1001
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ [SS_CLI_DEL_1001],
|
||||
+ dict(attrs=['l'], permissions='write'),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_DEL_1001,
|
||||
+ summary='Added selfservice "%s"' % SS_CLI_DEL_1001,
|
||||
+ result=dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_DEL_1001,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # del_1001: delete an existing rule
|
||||
+ dict(
|
||||
+ desc='del_1001: selfservice-del of an existing rule',
|
||||
+ command=('selfservice_del', [SS_CLI_DEL_1001], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=SS_CLI_DEL_1001,
|
||||
+ summary='Deleted selfservice "%s"' % SS_CLI_DEL_1001,
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # del_1002: delete a non-existent rule
|
||||
+ dict(
|
||||
+ desc='del_1002: selfservice-del of a non-existent rule',
|
||||
+ command=('selfservice_del', ['badname'], {}),
|
||||
+ expected=errors.NotFound(
|
||||
+ reason='ACI with name "badname" not found',
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ ]
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,166 +0,0 @@
|
||||
From d4d56a6705c870901bc73882e4804367f7c9c91a Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Sun, 1 Dec 2024 20:16:54 +0200
|
||||
Subject: [PATCH] ipalib/x509: support PyCA 44.0
|
||||
|
||||
PyCA made x509.Certificate class concrete, it cannot be extended anymore
|
||||
by Python code. The intent is to use helper functions to instantiate
|
||||
certificate objects and never create them directly.
|
||||
|
||||
FreeIPA wraps PyCA's x509.Certificate class and provides own shim
|
||||
on top of it. In most cases we load the certificate content via the
|
||||
helper functions and don't really need to derive from the certificate
|
||||
class.
|
||||
|
||||
Move IPACertificate to be a normal Python object class that stores
|
||||
x509.Certificate internally. The only place where this breaks is when
|
||||
IPACertificate object needs to be passed to a code that expects
|
||||
x509.Certificate (Dogtag PKI). In such cases, expose the underlying
|
||||
certificate instance via IPACertificate.cert property.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9708
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipalib/ipajson.py | 4 ++--
|
||||
ipalib/x509.py | 10 +++++++++-
|
||||
ipapython/ipaldap.py | 15 +++++++--------
|
||||
ipaserver/plugins/dogtag.py | 3 ++-
|
||||
4 files changed, 20 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/ipalib/ipajson.py b/ipalib/ipajson.py
|
||||
index 5551d12e5fec7e458fa6fe85560664b2fd897337..fd99c8219c722c52321336f28ff27e1573e906c7 100644
|
||||
--- a/ipalib/ipajson.py
|
||||
+++ b/ipalib/ipajson.py
|
||||
@@ -9,7 +9,7 @@ from decimal import Decimal
|
||||
import json
|
||||
import six
|
||||
from ipalib.constants import LDAP_GENERALIZED_TIME_FORMAT
|
||||
-from ipalib import capabilities
|
||||
+from ipalib import capabilities, x509
|
||||
from ipalib.x509 import Encoding as x509_Encoding
|
||||
from ipapython.dn import DN
|
||||
from ipapython.dnsutil import DNSName
|
||||
@@ -72,7 +72,7 @@ class _JSONPrimer(dict):
|
||||
list: self._enc_list,
|
||||
tuple: self._enc_list,
|
||||
dict: self._enc_dict,
|
||||
- crypto_x509.Certificate: self._enc_certificate,
|
||||
+ x509.IPACertificate: self._enc_certificate,
|
||||
crypto_x509.CertificateSigningRequest: self._enc_certificate,
|
||||
})
|
||||
|
||||
diff --git a/ipalib/x509.py b/ipalib/x509.py
|
||||
index fd08238962b2b5e9cd056fb13c0a81ee8f31b092..6780bead00b50efdf03c62ce717572eeb9df2e5f 100644
|
||||
--- a/ipalib/x509.py
|
||||
+++ b/ipalib/x509.py
|
||||
@@ -88,7 +88,7 @@ SAN_UPN = '1.3.6.1.4.1.311.20.2.3'
|
||||
SAN_KRB5PRINCIPALNAME = '1.3.6.1.5.2.2'
|
||||
|
||||
|
||||
-class IPACertificate(crypto_x509.Certificate):
|
||||
+class IPACertificate:
|
||||
"""
|
||||
A proxy class wrapping a python-cryptography certificate representation for
|
||||
IPA purposes
|
||||
@@ -205,6 +205,10 @@ class IPACertificate(crypto_x509.Certificate):
|
||||
"""
|
||||
return self._cert.fingerprint(algorithm)
|
||||
|
||||
+ @property
|
||||
+ def cert(self):
|
||||
+ return self._cert
|
||||
+
|
||||
@property
|
||||
def serial_number(self):
|
||||
return self._cert.serial_number
|
||||
@@ -457,6 +461,8 @@ def load_pem_x509_certificate(data):
|
||||
:returns: a ``IPACertificate`` object.
|
||||
:raises: ``ValueError`` if unable to load the certificate.
|
||||
"""
|
||||
+ if isinstance(data, IPACertificate):
|
||||
+ return data
|
||||
return IPACertificate(
|
||||
crypto_x509.load_pem_x509_certificate(data, backend=default_backend())
|
||||
)
|
||||
@@ -469,6 +475,8 @@ def load_der_x509_certificate(data):
|
||||
:returns: a ``IPACertificate`` object.
|
||||
:raises: ``ValueError`` if unable to load the certificate.
|
||||
"""
|
||||
+ if isinstance(data, IPACertificate):
|
||||
+ return data
|
||||
return IPACertificate(
|
||||
crypto_x509.load_der_x509_certificate(data, backend=default_backend())
|
||||
)
|
||||
diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py
|
||||
index 1888e40916aa6e641542f08fb30ff2b0d4b850b1..5bb81c1bc844fce9b14251d3702e09099d85cdb5 100644
|
||||
--- a/ipapython/ipaldap.py
|
||||
+++ b/ipapython/ipaldap.py
|
||||
@@ -33,7 +33,6 @@ import warnings
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
-from cryptography import x509 as crypto_x509
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
|
||||
import ldap
|
||||
@@ -748,10 +747,10 @@ class LDAPClient:
|
||||
'dnszoneidnsname': DNSName,
|
||||
'krbcanonicalname': Principal,
|
||||
'krbprincipalname': Principal,
|
||||
- 'usercertificate': crypto_x509.Certificate,
|
||||
- 'usercertificate;binary': crypto_x509.Certificate,
|
||||
- 'cACertificate': crypto_x509.Certificate,
|
||||
- 'cACertificate;binary': crypto_x509.Certificate,
|
||||
+ 'usercertificate': x509.IPACertificate,
|
||||
+ 'usercertificate;binary': x509.IPACertificate,
|
||||
+ 'cACertificate': x509.IPACertificate,
|
||||
+ 'cACertificate;binary': x509.IPACertificate,
|
||||
'nsds5replicalastupdatestart': unicode,
|
||||
'nsds5replicalastupdateend': unicode,
|
||||
'nsds5replicalastinitstart': unicode,
|
||||
@@ -1000,7 +999,7 @@ class LDAPClient:
|
||||
return dct
|
||||
elif isinstance(val, datetime):
|
||||
return val.strftime(LDAP_GENERALIZED_TIME_FORMAT).encode('utf-8')
|
||||
- elif isinstance(val, crypto_x509.Certificate):
|
||||
+ elif isinstance(val, x509.IPACertificate):
|
||||
return val.public_bytes(x509.Encoding.DER)
|
||||
elif val is None:
|
||||
return None
|
||||
@@ -1027,7 +1026,7 @@ class LDAPClient:
|
||||
return DNSName.from_text(val.decode('utf-8'))
|
||||
elif target_type in (DN, Principal):
|
||||
return target_type(val.decode('utf-8'))
|
||||
- elif target_type is crypto_x509.Certificate:
|
||||
+ elif target_type is x509.IPACertificate:
|
||||
return x509.load_der_x509_certificate(val)
|
||||
else:
|
||||
return target_type(val)
|
||||
@@ -1381,7 +1380,7 @@ class LDAPClient:
|
||||
]
|
||||
return cls.combine_filters(flts, rules)
|
||||
elif value is not None:
|
||||
- if isinstance(value, crypto_x509.Certificate):
|
||||
+ if isinstance(value, x509.IPACertificate):
|
||||
value = value.public_bytes(serialization.Encoding.DER)
|
||||
if isinstance(value, bytes):
|
||||
value = binascii.hexlify(value).decode('ascii')
|
||||
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
|
||||
index 78afb279795ecf74f296cbbb8724505075a6e4a9..ee6d0e347d640a2664e38ba64785c3d8af54bbad 100644
|
||||
--- a/ipaserver/plugins/dogtag.py
|
||||
+++ b/ipaserver/plugins/dogtag.py
|
||||
@@ -1581,7 +1581,8 @@ class kra(Backend):
|
||||
|
||||
crypto = cryptoutil.CryptographyCryptoProvider(
|
||||
transport_cert_nick="ra_agent",
|
||||
- transport_cert=x509.load_certificate_from_file(paths.RA_AGENT_PEM)
|
||||
+ transport_cert=x509.load_certificate_from_file(
|
||||
+ paths.RA_AGENT_PEM).cert
|
||||
)
|
||||
|
||||
# TODO: obtain KRA host & port from IPA service list or point to KRA load balancer
|
||||
--
|
||||
2.47.1
|
||||
|
||||
421
SOURCES/0033-ipatests-Additional-tests-for-32BitIdranges.patch
Normal file
421
SOURCES/0033-ipatests-Additional-tests-for-32BitIdranges.patch
Normal file
@ -0,0 +1,421 @@
|
||||
From b05586c2a6a81c7121dd40f8d627cd8a2c5908d8 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Wed, 18 Mar 2026 16:36:06 +0530
|
||||
Subject: [PATCH] ipatests: Additional tests for 32BitIdranges
|
||||
|
||||
Below tests are added
|
||||
|
||||
1. Create ipauser with 32bit id.
|
||||
2. Create ipagroup with 32Bit id.
|
||||
3. Create ipauser with 32Bit groupid range.
|
||||
4. Test ssh login with 32Bit id user.
|
||||
5. Test that ipauser with 32Bit is replicated.
|
||||
6. Test that 32Bit idrange is created in IPA-AD trust enviornment.
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
.../test_integration/test_32bit_idranges.py | 333 +++++++++++++++---
|
||||
1 file changed, 284 insertions(+), 49 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_32bit_idranges.py b/ipatests/test_integration/test_32bit_idranges.py
|
||||
index a928628d3..9b91fc618 100644
|
||||
--- a/ipatests/test_integration/test_32bit_idranges.py
|
||||
+++ b/ipatests/test_integration/test_32bit_idranges.py
|
||||
@@ -4,17 +4,85 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
+import re
|
||||
+
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
from ipatests.test_integration.test_trust import BaseTestTrust
|
||||
|
||||
+# The tests focus on 32-bit UID/GID creation and replication,
|
||||
+# SID behavior is not covered in the tests.
|
||||
+
|
||||
+# Range with First Posix ID >= 2^31 is considered a 32-bit range.
|
||||
+IDRANGE_32BIT_NAME = "{realm}_upper_32bit_range"
|
||||
+IDRANGE_32BIT_BASE_ID = 1 << 31 # 2147483648
|
||||
+
|
||||
+
|
||||
+def _32bit_idrange_exists(master):
|
||||
+ """
|
||||
+ Return True if an ipa-local range with base ID >= 2^31 already exists.
|
||||
+ """
|
||||
+ result = master.run_command(
|
||||
+ ["ipa", "idrange-find", "--type", "ipa-local"]
|
||||
+ )
|
||||
+ # Parse all "First Posix ID of the range: in the output'
|
||||
+ for match in re.finditer(
|
||||
+ r"First Posix ID of the range:\s*(\d+)",
|
||||
+ result.stdout_text
|
||||
+ ):
|
||||
+ if int(match.group(1)) >= IDRANGE_32BIT_BASE_ID:
|
||||
+ return True
|
||||
+ return False
|
||||
+
|
||||
+
|
||||
+def _add_32bit_idrange_if_missing(master):
|
||||
+ """
|
||||
+ Create the 32-bit ID range only if it does not already exist.
|
||||
+ Returns True if the range was added, False if it already existed.
|
||||
+ """
|
||||
+ if _32bit_idrange_exists(master):
|
||||
+ return False
|
||||
+ idrange = IDRANGE_32BIT_NAME.format(realm=master.domain.realm)
|
||||
+ id_length = 10000
|
||||
+ rid_base = 300_000_000
|
||||
+ secondary_rid_base = 500_000_000
|
||||
+ master.run_command(
|
||||
+ [
|
||||
+ "ipa",
|
||||
+ "idrange-add",
|
||||
+ idrange,
|
||||
+ "--base-id", str(IDRANGE_32BIT_BASE_ID),
|
||||
+ "--range-size", str(id_length),
|
||||
+ "--rid-base", str(rid_base),
|
||||
+ "--secondary-rid-base", str(secondary_rid_base),
|
||||
+ "--type=ipa-local"
|
||||
+ ]
|
||||
+ )
|
||||
+ # Restart dirsrv instance after the new idrange is added.
|
||||
+ tasks.restart_ipa_server(master)
|
||||
+ # Clear SSSD cache
|
||||
+ tasks.clear_sssd_cache(master)
|
||||
+ return True
|
||||
+
|
||||
|
||||
class Test32BitIdRanges(IntegrationTest):
|
||||
topology = "line"
|
||||
+ num_replicas = 1
|
||||
+ num_clients = 1
|
||||
+ # Counter for 32-bit UID/GID allocation; reset in install() so each
|
||||
+ # test run starts from 0 (install/uninstall gives a fresh environment).
|
||||
+ id_counter = 0
|
||||
+
|
||||
+ def get_next_32bit_id(self):
|
||||
+ """
|
||||
+ Generate unique 32-bit IDs for testing
|
||||
+ """
|
||||
+ self.id_counter += 1
|
||||
+ return IDRANGE_32BIT_BASE_ID + self.__class__.id_counter
|
||||
|
||||
def test_remove_subid_range(self):
|
||||
"""
|
||||
- Test that allocating subid will fail after disabling global option
|
||||
+ Test that allocating subids will fail after disabling the attribute
|
||||
"""
|
||||
master = self.master
|
||||
tasks.kinit_admin(master)
|
||||
@@ -23,19 +91,28 @@ class Test32BitIdRanges(IntegrationTest):
|
||||
master.run_command(
|
||||
["ipa", "config-mod", "--addattr", "ipaconfigstring=SubID:Disable"]
|
||||
)
|
||||
- master.run_command(["ipa", "idrange-del", idrange])
|
||||
+ master.run_command(
|
||||
+ ["ipa", "idrange-del", idrange]
|
||||
+ )
|
||||
+ master.run_command(["systemctl", "restart", "sssd"])
|
||||
|
||||
tasks.user_add(master, 'subiduser')
|
||||
- result = master.run_command(
|
||||
- ["ipa", "subid-generate", "--owner", "subiduser"], raiseonerr=False
|
||||
- )
|
||||
- assert result.returncode > 0
|
||||
- assert "Support for subordinate IDs is disabled" in result.stderr_text
|
||||
- tasks.user_del(master, 'subiduser')
|
||||
+ try:
|
||||
+ result = master.run_command(
|
||||
+ ["ipa", "subid-generate", "--owner", "subiduser"],
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+ assert result.returncode > 0
|
||||
+ assert "Support for subordinate IDs is disabled" in \
|
||||
+ result.stderr_text
|
||||
+ finally:
|
||||
+ # Cleanup: Remove test user
|
||||
+ tasks.user_del(master, 'subiduser')
|
||||
|
||||
def test_invoke_upgrader(self):
|
||||
- """Test that ipa-server-upgrade does not add subid ranges back"""
|
||||
-
|
||||
+ """
|
||||
+ Test that ipa-server-upgrade does not add subid ranges back.
|
||||
+ """
|
||||
master = self.master
|
||||
master.run_command(['ipa-server-upgrade'], raiseonerr=True)
|
||||
idrange = f"{master.domain.realm}_subid_range"
|
||||
@@ -58,69 +135,227 @@ class Test32BitIdRanges(IntegrationTest):
|
||||
assert "dnatype: " not in output
|
||||
|
||||
def test_create_user_with_32bit_id(self):
|
||||
- """Test that ID range above 2^31 can be used to assign IDs
|
||||
- to users and groups. Also check that SIDs generated properly.
|
||||
"""
|
||||
+ Test checks that 32Bit idrange is assigned to the user
|
||||
+ and getent passwd <username> returns the output.
|
||||
+ """
|
||||
+ master = self.master
|
||||
+ _add_32bit_idrange_if_missing(master)
|
||||
+
|
||||
+ uid = self.get_next_32bit_id()
|
||||
+ gid = self.get_next_32bit_id()
|
||||
+
|
||||
+ tasks.clear_sssd_cache(master)
|
||||
+ username = "user"
|
||||
+ tasks.create_active_user(
|
||||
+ master, username, "Secret123",
|
||||
+ extra_args=["--uid", str(uid), "--gid", str(gid)]
|
||||
+ )
|
||||
+ tasks.kinit_admin(master)
|
||||
+ try:
|
||||
+ result = master.run_command(
|
||||
+ ["ipa", "user-show", username, "--all", "--raw"]
|
||||
+ )
|
||||
+ assert result.returncode == 0, (
|
||||
+ f"User not found: {result.stderr_text}"
|
||||
+ )
|
||||
+ assert "ipantsecurityidentifier" in \
|
||||
+ result.stdout_text.lower(), (
|
||||
+ "SID not found in user entry"
|
||||
+ )
|
||||
+ if hasattr(self, 'clients') and self.clients:
|
||||
+ client = self.clients[0]
|
||||
+ tasks.clear_sssd_cache(client)
|
||||
+ result = client.run_command(
|
||||
+ ["getent", "passwd", username], raiseonerr=False
|
||||
+ )
|
||||
+ assert result.returncode == 0, (
|
||||
+ f"getent passwd failed: {result.stderr_text}"
|
||||
+ )
|
||||
+ assert str(uid) in result.stdout_text
|
||||
+ assert str(gid) in result.stdout_text
|
||||
+ finally:
|
||||
+ tasks.user_del(master, username)
|
||||
+
|
||||
+ def test_create_group_with_32bit_gid(self):
|
||||
+ """
|
||||
+ Test that a group can be created with a GID from the 32-bit range.
|
||||
+ """
|
||||
+ master = self.master
|
||||
+ groupname = 'grp32bit'
|
||||
+ gid = self.get_next_32bit_id()
|
||||
+ tasks.group_add(master, groupname, extra_args=["--gid", str(gid)])
|
||||
+ try:
|
||||
+ result = master.run_command(
|
||||
+ ["ipa", "group-show", groupname, "--all", "--raw"]
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ assert str(gid) in result.stdout_text, (
|
||||
+ f"GID {gid} not in group entry"
|
||||
+ )
|
||||
+ finally:
|
||||
+ tasks.group_del(master, groupname)
|
||||
|
||||
+ def test_user_in_group_with_32bit_ids(self):
|
||||
+ """
|
||||
+ Test user with 32-bit UID in a group with 32-bit GID.
|
||||
+ """
|
||||
master = self.master
|
||||
- idrange = f"{master.domain.realm}_upper_32bit_range"
|
||||
- id_base = 1 << 31
|
||||
- id_length = (1 << 31) - 2
|
||||
- uid = id_base + 1
|
||||
- gid = id_base + 1
|
||||
- master.run_command(
|
||||
- [
|
||||
- "ipa",
|
||||
- "idrange-add",
|
||||
- idrange,
|
||||
- "--base-id", str(id_base),
|
||||
- "--range-size", str(id_length),
|
||||
- "--rid-base", str(int(id_base >> 3)),
|
||||
- "--secondary-rid-base", str(int(id_base >> 3) + id_length),
|
||||
- "--type=ipa-local"
|
||||
- ]
|
||||
+ groupname = 'grp32bit2'
|
||||
+ username = 'user32bit'
|
||||
+ uid = self.get_next_32bit_id()
|
||||
+ gid = self.get_next_32bit_id()
|
||||
+ tasks.group_add(master, groupname, extra_args=["--gid", str(gid)])
|
||||
+ tasks.create_active_user(
|
||||
+ master, username, "Secret123",
|
||||
+ extra_args=["--uid", str(uid), "--gid", str(gid)]
|
||||
)
|
||||
+ tasks.kinit_admin(master)
|
||||
+ try:
|
||||
+ tasks.group_add_member(master, groupname, users=username)
|
||||
+ result = master.run_command(
|
||||
+ ["ipa", "group-show", groupname, "--all", "--raw"]
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ assert username in result.stdout_text
|
||||
+ assert f"gidnumber: {gid}" in result.stdout_text, (
|
||||
+ f"GID {gid} not found in group entry"
|
||||
+ )
|
||||
+ assert "ipaNTSecurityIdentifier:" in result.stdout_text, (
|
||||
+ "Group does not contain a SID"
|
||||
+ )
|
||||
+ result = master.run_command(
|
||||
+ ["ipa", "user-show", username, "--all", "--raw"]
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ finally:
|
||||
+ master.run_command(
|
||||
+ ["ipa", "group-remove-member", groupname,
|
||||
+ "--users", username],
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+ tasks.user_del(master, username)
|
||||
+ tasks.group_del(master, groupname)
|
||||
|
||||
- # We added new ID range, SIDGEN will only take it after
|
||||
- # restarting a directory server instance.
|
||||
- tasks.restart_ipa_server(master)
|
||||
+ def test_ssh_login_with_32bit_id(self):
|
||||
+ """
|
||||
+ Test that a user with 32-bit UID/GID can kinit and log in via SSH
|
||||
+ from the client to the master using GSSAPI (Kerberos).
|
||||
+ """
|
||||
+ client = self.clients[0]
|
||||
+ master = self.master
|
||||
+ testuser = 'sshuser32bit'
|
||||
+ password = 'Secret123'
|
||||
+ uid = self.get_next_32bit_id()
|
||||
+ gid = self.get_next_32bit_id()
|
||||
|
||||
- # Clear SSSD cache to pick up new ID range
|
||||
tasks.clear_sssd_cache(master)
|
||||
+ tasks.create_active_user(
|
||||
+ master, testuser, password,
|
||||
+ extra_args=["--uid", str(uid), "--gid", str(gid)]
|
||||
+ )
|
||||
+ tasks.kinit_admin(master)
|
||||
+ hbac_rule = "allow_ssh_32bit_test"
|
||||
+ tasks.hbacrule_add(master, hbac_rule, extra_args=["--hostcat=all"])
|
||||
+ tasks.hbacrule_add_user(master, hbac_rule, users=testuser)
|
||||
+ tasks.hbacrule_add_service(master, hbac_rule, services="sshd")
|
||||
+ try:
|
||||
+ result = master.run_command(
|
||||
+ ["ipa", "user-show", testuser, "--all", "--raw"]
|
||||
+ )
|
||||
+ assert result.returncode == 0, (
|
||||
+ f"User {testuser} not found: {result.stderr_text}"
|
||||
+ )
|
||||
|
||||
- tasks.user_add(master, "user", extra_args=[
|
||||
- "--uid", str(uid), "--gid", str(gid)
|
||||
- ])
|
||||
+ tasks.clear_sssd_cache(client)
|
||||
+ tasks.clear_sssd_cache(master)
|
||||
+ tasks.kdestroy_all(client)
|
||||
+ tasks.kinit_as_user(client, testuser, password)
|
||||
+ result = client.run_command([
|
||||
+ 'ssh', '-o', 'StrictHostKeyChecking=no', '-K',
|
||||
+ '-l', testuser, master.hostname, 'echo login successful'
|
||||
+ ], raiseonerr=False)
|
||||
+ assert result.returncode == 0, (
|
||||
+ "SSH (GSSAPI) from client to master failed: "
|
||||
+ f"{result.stderr_text}"
|
||||
+ )
|
||||
+ assert 'login successful' in result.stdout_text, (
|
||||
+ "SSH succeeded but expected output missing: "
|
||||
+ f"{result.stdout_text}"
|
||||
+ )
|
||||
+ finally:
|
||||
+ tasks.kdestroy_all(client)
|
||||
+ master.run_command(
|
||||
+ ["ipa", "hbacrule-del", hbac_rule], raiseonerr=False
|
||||
+ )
|
||||
+ tasks.kinit_admin(master)
|
||||
+ tasks.user_del(master, testuser)
|
||||
|
||||
- result = master.run_command(
|
||||
- ["ipa", "user-show", "user", "--all", "--raw"], raiseonerr=False
|
||||
- )
|
||||
- assert result.returncode == 0
|
||||
- assert "ipaNTSecurityIdentifier:" in result.stdout_text
|
||||
+ def test_32bit_id_replication(self):
|
||||
+ """
|
||||
+ Test that users with 32-bit IDs replicate correctly
|
||||
+ """
|
||||
+ master = self.master
|
||||
+ replica = self.replicas[0]
|
||||
+ tasks.kinit_admin(master)
|
||||
+ testuser = 'repluser32bit'
|
||||
+ uid = self.get_next_32bit_id()
|
||||
+ gid = self.get_next_32bit_id()
|
||||
|
||||
- result = master.run_command(
|
||||
- ["id", "user"], raiseonerr=False
|
||||
+ tasks.clear_sssd_cache(master)
|
||||
+
|
||||
+ # Create user on master
|
||||
+ tasks.create_active_user(
|
||||
+ master, testuser, "Secret123",
|
||||
+ extra_args=["--uid", str(uid), "--gid", str(gid)]
|
||||
)
|
||||
- assert result.returncode == 0
|
||||
- assert str(uid) in result.stdout_text
|
||||
+ tasks.kinit_admin(master)
|
||||
+ try:
|
||||
+ tasks.wait_for_replication(master.ldap_connect())
|
||||
+
|
||||
+ result = master.run_command(
|
||||
+ ["ipa", "user-show", testuser, "--all", "--raw"],
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+ assert result.returncode == 0, (
|
||||
+ f"User {testuser} not found on master"
|
||||
+ )
|
||||
+ assert str(uid) in result.stdout_text, (
|
||||
+ f"UID {uid} not on master"
|
||||
+ )
|
||||
+
|
||||
+ tasks.kinit_admin(replica)
|
||||
+ result = replica.run_command(
|
||||
+ ["ipa", "user-show", testuser, "--all", "--raw"],
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+ assert result.returncode == 0, (
|
||||
+ f"User {testuser} not replicated to replica"
|
||||
+ )
|
||||
+ assert str(uid) in result.stdout_text, (
|
||||
+ f"UID {uid} not on replica"
|
||||
+ )
|
||||
+ finally:
|
||||
+ # Cleanup: Remove test user from master
|
||||
+ tasks.kinit_admin(master)
|
||||
+ tasks.user_del(master, testuser)
|
||||
|
||||
|
||||
class Test32BitIdrangeInTrustEnv(Test32BitIdRanges, BaseTestTrust):
|
||||
"""
|
||||
Tests to check 32BitIdrange functionality
|
||||
- in IPA-AD trust enviornment
|
||||
+ in IPA-AD trust environment
|
||||
"""
|
||||
topology = 'line'
|
||||
+ num_replicas = 1
|
||||
num_ad_domains = 1
|
||||
num_ad_subdomains = 0
|
||||
num_ad_treedomains = 0
|
||||
- num_clients = 0
|
||||
+ num_clients = 1
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
- super(BaseTestTrust, cls).install(mh)
|
||||
+ super(Test32BitIdrangeInTrustEnv, cls).install(mh)
|
||||
cls.ad = cls.ads[0]
|
||||
- cls.ad_domain = cls.ad.domain.name
|
||||
tasks.configure_dns_for_trust(cls.master, cls.ad)
|
||||
- tasks.install_adtrust(cls.master)
|
||||
tasks.establish_trust_with_ad(cls.master, cls.ad.domain.name)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,260 @@
|
||||
From 3acf55ed7fcf9a3b38deb0efb98d222d57bafbbc Mon Sep 17 00:00:00 2001
|
||||
From: Anuja More <amore@redhat.com>
|
||||
Date: Thu, 26 Feb 2026 12:04:50 +0530
|
||||
Subject: [PATCH] ipatests: add HTTP GSSAPI Kerberos authentication
|
||||
tests with AD trust
|
||||
|
||||
Add TestTrustFunctionalHttp integration test class covering GSSAPI-protected
|
||||
HTTP access in an AD trust environment:
|
||||
|
||||
- test_ipa_trust_func_http_krb_ipauser: IPA user with a valid Kerberos ticket
|
||||
can access the GSSAPI-protected webapp; AD root and subdomain users are
|
||||
denied when only the IPA user is in the Allow list
|
||||
- test_ipa_trust_func_http_krb_aduser: AD root domain and subdomain users with
|
||||
valid Kerberos tickets can access the webapp; IPA users are denied when the
|
||||
Allow list is configured for an AD user
|
||||
- test_ipa_trust_func_http_krb_nouser: a user without a Kerberos ticket
|
||||
receives a 401 Unauthorized response
|
||||
|
||||
The class sets up an Apache httpd instance with mod_auth_gssapi on the IPA
|
||||
client, obtains a service keytab via ipa-getkeytab, and uses curl with
|
||||
GSSAPI negotiate to drive each scenario.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9845
|
||||
Assisted-by: Claude noreply@anthropic.com
|
||||
Signed-off-by: Anuja More <amore@redhat.com>
|
||||
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
.../test_integration/test_trust_functional.py | 209 +++++++++++++++++-
|
||||
1 file changed, 208 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_trust_functional.py b/ipatests/test_integration/test_trust_functional.py
|
||||
index a85f21e96463757b9a446df666d5361e65ba686c..5a9eae8cebfdae23bf37d3298e455bab8e3304ef 100644
|
||||
--- a/ipatests/test_integration/test_trust_functional.py
|
||||
+++ b/ipatests/test_integration/test_trust_functional.py
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
+import re
|
||||
import time
|
||||
-
|
||||
+import textwrap
|
||||
from ipaplatform.paths import paths
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
from ipatests.test_integration.test_trust import BaseTestTrust
|
||||
@@ -658,3 +659,209 @@ class TestTrustFunctionalSudo(BaseTestTrust):
|
||||
raiseonerr=False)
|
||||
finally:
|
||||
self._cleanup_srule(srule)
|
||||
+
|
||||
+
|
||||
+class TestTrustFunctionalHttp(BaseTestTrust):
|
||||
+ topology = 'line'
|
||||
+ num_ad_treedomains = 0
|
||||
+
|
||||
+ ad_user_password = 'Secret123'
|
||||
+
|
||||
+ # Apache configuration for GSSAPI-protected webapp. The /mywebapp
|
||||
+ # location requires Kerberos authentication and restricts access by
|
||||
+ # domain: IPA users (@IPA_REALM) or AD users (@AD_DOMAIN).
|
||||
+ apache_conf = textwrap.dedent('''
|
||||
+ Alias /mywebapp "/var/www/html/mywebapp"
|
||||
+ <Directory "/var/www/html/mywebapp">
|
||||
+ Allow from all
|
||||
+ </Directory>
|
||||
+ <Location "/mywebapp">
|
||||
+ LogLevel debug
|
||||
+ AuthType GSSAPI
|
||||
+ AuthName "IPA Kerberos authentication"
|
||||
+ GssapiNegotiateOnce on
|
||||
+ GssapiBasicAuthMech krb5
|
||||
+ GssapiCredStore keytab:{keytab_path}
|
||||
+ <RequireAll>
|
||||
+ Require valid-user
|
||||
+ # Require expr: restrict access by domain. REMOTE_USER is set by
|
||||
+ # mod_auth_gssapi after GSSAPI authentication. Allow users whose
|
||||
+ # principal ends with the domain (IPA realm or AD domain).
|
||||
+ Require expr %{{REMOTE_USER}} =~ /{allowed_domain_regex}$/
|
||||
+ </RequireAll>
|
||||
+ </Location>
|
||||
+ ''')
|
||||
+
|
||||
+ def _configure_webapp(self, allowed_domain):
|
||||
+ """Write the GSSAPI vhost config and restart httpd on the client.
|
||||
+
|
||||
+ allowed_domain: realm/domain for access control (e.g. IPA.TEST for
|
||||
+ IPA users, AD.DOMAIN for AD users). Users whose principal ends with
|
||||
+ @allowed_domain are granted access.
|
||||
+ """
|
||||
+ # Escape dots for regex (e.g. IPA.TEST -> IPA\\.TEST)
|
||||
+ escaped = re.escape(allowed_domain)
|
||||
+ allowed_domain_regex = '.*@' + escaped
|
||||
+ keytab_path = f"/etc/httpd/conf/{self.clients[0].hostname}.keytab"
|
||||
+ self.clients[0].put_file_contents(
|
||||
+ '/etc/httpd/conf.d/mywebapp.conf',
|
||||
+ self.apache_conf.format(
|
||||
+ keytab_path=keytab_path,
|
||||
+ allowed_domain_regex=allowed_domain_regex,
|
||||
+ )
|
||||
+ )
|
||||
+ self.clients[0].run_command(['systemctl', 'restart', 'httpd'])
|
||||
+
|
||||
+ def _assert_curl_ok(self, msg=None):
|
||||
+ """Run curl with GSSAPI negotiate and assert the webapp responds."""
|
||||
+ url = f"http://{self.clients[0].hostname}/mywebapp/index.html"
|
||||
+ result = self.clients[0].run_command([
|
||||
+ paths.BIN_CURL, '-v', '--negotiate', '-u:', url
|
||||
+ ])
|
||||
+ assert "TEST_MY_WEB_APP" in result.stdout_text, (
|
||||
+ msg or f"Expected webapp content at {url}"
|
||||
+ )
|
||||
+
|
||||
+ def _assert_curl_GSSAPI_access_denied(self, msg=None):
|
||||
+ """Run curl with GSSAPI negotiate and assert a 401 is returned."""
|
||||
+ url = f"http://{self.clients[0].hostname}/mywebapp/index.html"
|
||||
+ result = self.clients[0].run_command([
|
||||
+ paths.BIN_CURL, '-v', '--negotiate', '-u:', url
|
||||
+ ], raiseonerr=False)
|
||||
+ output = f"{result.stdout_text}{result.stderr_text}"
|
||||
+ assert ("401" in output
|
||||
+ or "Unauthorized" in output
|
||||
+ or "Authorization Required" in output), (
|
||||
+ msg or f"Expected 401/Unauthorized at {url}, got: {output[:200]}"
|
||||
+ )
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ """Extend base install to configure Apache/GSSAPI for HTTP tests.
|
||||
+
|
||||
+ Runs once before any test in this class. Sets up the AD trust,
|
||||
+ creates the HTTP service principal and IPA test user, installs
|
||||
+ mod_auth_gssapi, retrieves the service keytab, and provisions the
|
||||
+ static webapp content used by all HTTP tests.
|
||||
+ """
|
||||
+ super().install(mh)
|
||||
+ tasks.configure_dns_for_trust(cls.master, cls.ad)
|
||||
+ tasks.establish_trust_with_ad(
|
||||
+ cls.master, cls.ad_domain,
|
||||
+ extra_args=['--range-type', 'ipa-ad-trust'])
|
||||
+
|
||||
+ # Create HTTP service principal on master
|
||||
+ service_principal = f"HTTP/{cls.clients[0].hostname}"
|
||||
+ cls.master.run_command(
|
||||
+ ["ipa", "service-add", service_principal]
|
||||
+ )
|
||||
+
|
||||
+ # Create IPA user for HTTP tests
|
||||
+ tasks.create_active_user(
|
||||
+ cls.master, "ipahttpuser1", password="Passw0rd1",
|
||||
+ first="f", last="l"
|
||||
+ )
|
||||
+
|
||||
+ # Clear SSSD cache on master
|
||||
+ tasks.clear_sssd_cache(cls.master)
|
||||
+ tasks.wait_for_sssd_domain_status_online(cls.master)
|
||||
+
|
||||
+ # Install Apache and the GSSAPI module on the IPA client
|
||||
+ tasks.install_packages(
|
||||
+ cls.clients[0], ['mod_auth_gssapi', 'httpd']
|
||||
+ )
|
||||
+
|
||||
+ # Retrieve and protect the HTTP service keytab
|
||||
+ keytab_path = f"/etc/httpd/conf/{cls.clients[0].hostname}.keytab"
|
||||
+ cls.clients[0].run_command([
|
||||
+ 'ipa-getkeytab', '-s', cls.master.hostname,
|
||||
+ '-k', keytab_path,
|
||||
+ '-p', service_principal
|
||||
+ ])
|
||||
+ cls.clients[0].run_command(
|
||||
+ ['chown', 'apache:apache', keytab_path]
|
||||
+ )
|
||||
+
|
||||
+ # Create webapp directory and static content
|
||||
+ cls.clients[0].run_command(
|
||||
+ ['mkdir', '-p', '/var/www/html/mywebapp']
|
||||
+ )
|
||||
+ cls.clients[0].put_file_contents(
|
||||
+ '/var/www/html/mywebapp/index.html',
|
||||
+ 'TEST_MY_WEB_APP\n'
|
||||
+ )
|
||||
+
|
||||
+ def test_ipa_trust_func_http_krb_ipauser(self):
|
||||
+ """
|
||||
+ Test IPA User access http with kerberos ticket via valid user.
|
||||
+
|
||||
+ This test verifies that an IPA user with a valid Kerberos ticket
|
||||
+ can successfully access an HTTP resource protected by GSSAPI
|
||||
+ authentication and restricted to IPA users.
|
||||
+ """
|
||||
+ ipa_realm = self.clients[0].domain.realm
|
||||
+ self._configure_webapp(ipa_realm)
|
||||
+
|
||||
+ tasks.kdestroy_all(self.clients[0])
|
||||
+ tasks.kinit_as_user(
|
||||
+ self.clients[0], f'ipahttpuser1@{ipa_realm}', "Passw0rd1"
|
||||
+ )
|
||||
+
|
||||
+ self._assert_curl_ok()
|
||||
+
|
||||
+ users = [
|
||||
+ (self.aduser, self.ad_domain),
|
||||
+ (self.subaduser, self.ad_subdomain),
|
||||
+ ]
|
||||
+ for aduser, domain in users:
|
||||
+ tasks.kdestroy_all(self.clients[0])
|
||||
+ # pylint: disable=use-maxsplit-arg
|
||||
+ principal = f"{aduser.split('@')[0]}@{domain.upper()}"
|
||||
+ tasks.kinit_as_user(
|
||||
+ self.clients[0], principal, self.ad_user_password
|
||||
+ )
|
||||
+ self._assert_curl_GSSAPI_access_denied(
|
||||
+ msg=f"Expected 401 for AD user {aduser}"
|
||||
+ )
|
||||
+
|
||||
+ def test_ipa_trust_func_http_krb_aduser(self):
|
||||
+ """
|
||||
+ Test AD root and subdomain users access http with kerberos ticket.
|
||||
+
|
||||
+ This test verifies that both a root AD domain user and a child
|
||||
+ subdomain user with valid Kerberos tickets can successfully access
|
||||
+ an HTTP resource protected by GSSAPI authentication and restricted
|
||||
+ to AD domain / AD subdomain users.
|
||||
+ """
|
||||
+ users = [
|
||||
+ (self.aduser, self.ad_domain),
|
||||
+ (self.subaduser, self.ad_subdomain),
|
||||
+ ]
|
||||
+ for aduser, domain in users:
|
||||
+ tasks.kdestroy_all(self.clients[0])
|
||||
+ # pylint: disable=use-maxsplit-arg
|
||||
+ principal = f"{aduser.split('@')[0]}@{domain.upper()}"
|
||||
+ self._configure_webapp(domain.upper())
|
||||
+ tasks.kinit_as_user(
|
||||
+ self.clients[0], principal, self.ad_user_password
|
||||
+ )
|
||||
+ self._assert_curl_ok(
|
||||
+ msg=f"Expected webapp content for AD user {aduser}"
|
||||
+ )
|
||||
+ tasks.kdestroy_all(self.clients[0])
|
||||
+ tasks.kinit_as_user(self.clients[0], "ipahttpuser1", "Passw0rd1")
|
||||
+ self._assert_curl_GSSAPI_access_denied(
|
||||
+ msg=f"Expected 401 for IPA user after AD user {aduser}"
|
||||
+ )
|
||||
+
|
||||
+ def test_ipa_trust_func_http_krb_nouser(self):
|
||||
+ """
|
||||
+ Test User cannot access http without kerberos ticket via valid user.
|
||||
+
|
||||
+ This test verifies that an user without a valid Kerberos ticket
|
||||
+ is denied access to an HTTP resource protected by GSSAPI
|
||||
+ authentication, receiving a 401 Unauthorized error.
|
||||
+ """
|
||||
+ tasks.kdestroy_all(self.clients[0])
|
||||
+
|
||||
+ self._assert_curl_GSSAPI_access_denied()
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,120 +0,0 @@
|
||||
From 8dfec28647f7c17e47fbfc96a1720dcde1592386 Mon Sep 17 00:00:00 2001
|
||||
From: Stanislav Levin <slev@altlinux.org>
|
||||
Date: Mon, 2 Dec 2024 15:04:30 +0300
|
||||
Subject: [PATCH] pyca: adapt import paths for TripleDES cipher
|
||||
|
||||
https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.algorithms.TripleDES
|
||||
|
||||
> This algorithm has been deprecated and moved to the Decrepit
|
||||
cryptography module. If you need to continue using it then update your
|
||||
code to use the new module path. It will be removed from this namespace
|
||||
in 48.0.0.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9708
|
||||
Signed-off-by: Stanislav Levin <slev@altlinux.org>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipaclient/plugins/vault.py | 8 +++++++-
|
||||
ipalib/constants.py | 24 +++++++++++-------------
|
||||
ipaserver/install/ipa_otptoken_import.py | 8 +++++++-
|
||||
3 files changed, 25 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py
|
||||
index 75415c03a57242ae674636fa31a72db2fa56d6ea..6af7297936924dfb80e7f79924b570421da65c97 100644
|
||||
--- a/ipaclient/plugins/vault.py
|
||||
+++ b/ipaclient/plugins/vault.py
|
||||
@@ -34,6 +34,12 @@ from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
+try:
|
||||
+ # cryptography>=43.0.0
|
||||
+ from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
|
||||
+except ImportError:
|
||||
+ # will be removed from this module in cryptography 48.0.0
|
||||
+ from cryptography.hazmat.primitives.ciphers.algorithms import TripleDES
|
||||
from cryptography.hazmat.primitives.padding import PKCS7
|
||||
from cryptography.hazmat.primitives.serialization import (
|
||||
load_pem_public_key, load_pem_private_key)
|
||||
@@ -661,7 +667,7 @@ class ModVaultData(Local):
|
||||
if name == constants.VAULT_WRAPPING_AES128_CBC:
|
||||
return algorithms.AES(os.urandom(128 // 8))
|
||||
elif name == constants.VAULT_WRAPPING_3DES:
|
||||
- return algorithms.TripleDES(os.urandom(196 // 8))
|
||||
+ return TripleDES(os.urandom(196 // 8))
|
||||
else:
|
||||
# unreachable
|
||||
raise ValueError(name)
|
||||
diff --git a/ipalib/constants.py b/ipalib/constants.py
|
||||
index b657e5a9065d115d0eff2dbfffff49e992006536..c90caa22149ec3d93d45fcb5480f7401e4555799 100644
|
||||
--- a/ipalib/constants.py
|
||||
+++ b/ipalib/constants.py
|
||||
@@ -25,20 +25,19 @@ All constants centralised in one file.
|
||||
import os
|
||||
import string
|
||||
import uuid
|
||||
-import warnings
|
||||
-
|
||||
-warnings.filterwarnings(
|
||||
- "ignore",
|
||||
- "TripleDES has been moved to "
|
||||
- "cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and "
|
||||
- "will be removed from this module in 48.0.0",
|
||||
- category=UserWarning)
|
||||
|
||||
from ipaplatform.constants import constants as _constants
|
||||
from ipapython.dn import DN
|
||||
from ipapython.fqdn import gethostfqdn
|
||||
from ipapython.version import VERSION, API_VERSION
|
||||
-from cryptography.hazmat.primitives.ciphers import algorithms, modes
|
||||
+from cryptography.hazmat.primitives.ciphers import modes
|
||||
+try:
|
||||
+ # cryptography>=43.0.0
|
||||
+ from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
|
||||
+except ImportError:
|
||||
+ # will be removed from this module in cryptography 48.0.0
|
||||
+ from cryptography.hazmat.primitives.ciphers.algorithms import TripleDES
|
||||
+
|
||||
from cryptography.hazmat.backends.openssl.backend import backend
|
||||
|
||||
|
||||
@@ -389,7 +388,6 @@ VAULT_WRAPPING_SUPPORTED_ALGOS = (
|
||||
VAULT_WRAPPING_DEFAULT_ALGO = VAULT_WRAPPING_AES128_CBC
|
||||
|
||||
# Add 3DES for backwards compatibility if supported
|
||||
-if getattr(algorithms, 'TripleDES', None):
|
||||
- if backend.cipher_supported(algorithms.TripleDES(
|
||||
- b"\x00" * 8), modes.CBC(b"\x00" * 8)):
|
||||
- VAULT_WRAPPING_SUPPORTED_ALGOS += (VAULT_WRAPPING_3DES,)
|
||||
+if backend.cipher_supported(TripleDES(
|
||||
+ b"\x00" * 8), modes.CBC(b"\x00" * 8)):
|
||||
+ VAULT_WRAPPING_SUPPORTED_ALGOS += (VAULT_WRAPPING_3DES,)
|
||||
diff --git a/ipaserver/install/ipa_otptoken_import.py b/ipaserver/install/ipa_otptoken_import.py
|
||||
index 279a7502d2f305309252b3b291e32b772a51a1d3..17457f6c5b81ab70a0ecee13bf744e242ec88ff0 100644
|
||||
--- a/ipaserver/install/ipa_otptoken_import.py
|
||||
+++ b/ipaserver/install/ipa_otptoken_import.py
|
||||
@@ -37,6 +37,12 @@ from cryptography.hazmat.primitives import hashes, hmac
|
||||
from cryptography.hazmat.primitives.padding import PKCS7
|
||||
from cryptography.hazmat.primitives.kdf import pbkdf2
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
+try:
|
||||
+ # cryptography>=43.0.0
|
||||
+ from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
|
||||
+except ImportError:
|
||||
+ # will be removed from this module in cryptography 48.0.0
|
||||
+ from cryptography.hazmat.primitives.ciphers.algorithms import TripleDES
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
@@ -169,7 +175,7 @@ def convertAlgorithm(value):
|
||||
# in the list of the vault wrapping algorithms, we cannot use 3DES anywhere
|
||||
if VAULT_WRAPPING_3DES in VAULT_WRAPPING_SUPPORTED_ALGOS:
|
||||
supported_algs["http://www.w3.org/2001/04/xmlenc#tripledes-cbc"] = (
|
||||
- algorithms.TripleDES, modes.CBC, 64)
|
||||
+ TripleDES, modes.CBC, 64)
|
||||
|
||||
return supported_algs.get(value.lower(), (None, None, None))
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,134 +0,0 @@
|
||||
From 3e7ec3dc49d0f559bdbe330e52019e59f0b57c18 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Tue, 3 Dec 2024 18:06:45 +0200
|
||||
Subject: [PATCH] ipa-pwd-extop: clarify OTP use over LDAP binds
|
||||
|
||||
OTP use during LDAP bind can be enforced either explicitly via client
|
||||
specifying a control with OID 2.16.840.1.113730.3.8.10.7 and no payload
|
||||
or implicitly through the global IPA configuration with EnforceLDAPOTP.
|
||||
|
||||
OTP token enforcement overrides IPA user authentication types
|
||||
requirements:
|
||||
|
||||
If OTP enforcement is required:
|
||||
|
||||
- if user authentication types still allow password authentication,
|
||||
authentication with just a password is denied, regardless whether OTP
|
||||
tokens are associated with the user or not.
|
||||
|
||||
If OTP enforcement is not required:
|
||||
|
||||
- if user has no OTP tokens but user authentication types require OTP
|
||||
use, authentication with just a password is allowed until a token is
|
||||
added.
|
||||
|
||||
- if user has OTP tokens and user authentication types require OTP use
|
||||
but not password, authentication with just a password is denied.
|
||||
|
||||
Additionally, enforcement of OTP only applies to LDAP objects which
|
||||
don't use 'simpleSecurityObject' objectclass. This allows system service
|
||||
accounts to continue authenticate with a password regardless of the
|
||||
OTP enforcement.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9699
|
||||
Fixes: https://pagure.io/freeipa/issue/9711
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
.../ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 38 +++++++++++++++----
|
||||
1 file changed, 30 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
index 1c1340e31ac30cb01412a7065ea339cb5461e839..42e880fd0a5c8b4708b145b340209eb218f60c4e 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
@@ -1219,12 +1219,10 @@ typedef enum {
|
||||
} otp_req_enum;
|
||||
static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
|
||||
struct berval *creds, otp_req_enum otpreq,
|
||||
- bool *notokens)
|
||||
+ bool *notokens, uint32_t *auth_types)
|
||||
{
|
||||
- uint32_t auth_types;
|
||||
-
|
||||
/* Get the configured authentication types. */
|
||||
- auth_types = otp_config_auth_types(otp_config, entry);
|
||||
+ *auth_types = otp_config_auth_types(otp_config, entry);
|
||||
*notokens = false;
|
||||
|
||||
/*
|
||||
@@ -1237,7 +1235,8 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
|
||||
* 2. If PWD is enabled or OTP succeeded, fall through to PWD validation.
|
||||
*/
|
||||
|
||||
- if (auth_types & OTP_CONFIG_AUTH_TYPE_OTP) {
|
||||
+ if ((*auth_types & OTP_CONFIG_AUTH_TYPE_OTP) ||
|
||||
+ (otpreq != OTP_IS_NOT_REQUIRED)) {
|
||||
struct otp_token **tokens = NULL;
|
||||
|
||||
LOG_PLUGIN_NAME(IPAPWD_PLUGIN_NAME,
|
||||
@@ -1270,7 +1269,7 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry,
|
||||
otp_token_free_array(tokens);
|
||||
}
|
||||
|
||||
- return (auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) &&
|
||||
+ return (*auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) &&
|
||||
(otpreq == OTP_IS_NOT_REQUIRED);
|
||||
}
|
||||
|
||||
@@ -1451,6 +1450,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
struct ipapwd_krbcfg *krbcfg = NULL;
|
||||
struct berval *credentials = NULL;
|
||||
Slapi_Entry *entry = NULL;
|
||||
+ Slapi_Value *objectclass = NULL;
|
||||
Slapi_DN *target_sdn = NULL;
|
||||
Slapi_DN *sdn = NULL;
|
||||
const char *dn = NULL;
|
||||
@@ -1465,6 +1465,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
int rc = LDAP_INVALID_CREDENTIALS;
|
||||
char *errMesg = NULL;
|
||||
bool notokens = false;
|
||||
+ uint32_t auth_types = 0;
|
||||
|
||||
/* get BIND parameters */
|
||||
ret |= slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &target_sdn);
|
||||
@@ -1538,12 +1539,33 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
otpreq = OTP_IS_REQUIRED_IMPLICITLY;
|
||||
}
|
||||
}
|
||||
+ /* we only apply OTP policy to Kerberos principals */
|
||||
+ objectclass = slapi_value_new_string("krbprincipalaux");
|
||||
+ if (objectclass == NULL) {
|
||||
+ goto invalid_creds;
|
||||
+ }
|
||||
+ if (!slapi_entry_attr_has_syntax_value(entry, SLAPI_ATTR_OBJECTCLASS,
|
||||
+ objectclass)) {
|
||||
+ otpreq = OTP_IS_NOT_REQUIRED;
|
||||
+ }
|
||||
+ slapi_value_free(&objectclass);
|
||||
+
|
||||
if (!syncreq && !ipapwd_pre_bind_otp(dn, entry,
|
||||
- credentials, otpreq, ¬okens)) {
|
||||
+ credentials, otpreq,
|
||||
+ ¬okens, &auth_types)) {
|
||||
/* We got here because ipapwd_pre_bind_otp() returned false,
|
||||
* it means that either token verification failed or
|
||||
* a rule for empty tokens failed current policy. */
|
||||
- if (!(notokens || (otpreq == OTP_IS_NOT_REQUIRED)))
|
||||
+
|
||||
+ /* Check if there were any tokens associated, thus
|
||||
+ * OTP token verification has really failed */
|
||||
+ if (notokens == false)
|
||||
+ goto invalid_creds;
|
||||
+
|
||||
+ /* No tokens, check if auth type does not include OTP but OTP is
|
||||
+ * enforced by the current policy */
|
||||
+ if (!(auth_types & OTP_CONFIG_AUTH_TYPE_OTP) &&
|
||||
+ (otpreq != OTP_IS_NOT_REQUIRED))
|
||||
goto invalid_creds;
|
||||
}
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
||||
2595
SOURCES/0035-ipatests-Extend-netgroup-test-coverage.patch
Normal file
2595
SOURCES/0035-ipatests-Extend-netgroup-test-coverage.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,45 +0,0 @@
|
||||
From 477dbba18bf987bf4461fdfdfba0d497159db7ce Mon Sep 17 00:00:00 2001
|
||||
From: Stanislav Levin <slev@altlinux.org>
|
||||
Date: Wed, 4 Dec 2024 19:56:51 +0300
|
||||
Subject: [PATCH] adtrust: add missing ipaAllowedOperations objectclass
|
||||
|
||||
Per @abbra explanation:
|
||||
> When expected Kerberos principal names for this object were flipped to
|
||||
follow requirements for cross-realm krbtgt objects expected by Active
|
||||
Directory, trusted object changed its canonical Kerberos principal name.
|
||||
The keytab for this Kerberos principal name is fetched by SSSD and it
|
||||
needs to be permitted to read the key. We added the virtual permission
|
||||
to allow the keytab retrieval but didn't add the objectclass that
|
||||
actually allows adding an LDAP attribute to express the permission. When
|
||||
an attribute is added to an LDAP object, objectclasses of the object
|
||||
must allow presence of that attribute.
|
||||
|
||||
This is the followup to #9471 and fixes the upgrade.
|
||||
|
||||
Thanks @abbra!
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9471
|
||||
Fixes: https://pagure.io/freeipa/issue/9712
|
||||
Signed-off-by: Stanislav Levin <slev@altlinux.org>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipaserver/install/plugins/adtrust.py | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
|
||||
index e6d49cb2512bff7dcce57f019ecb6c497d11ed52..ab3d427ef561aeb26eb098270446640ba451c8ad 100644
|
||||
--- a/ipaserver/install/plugins/adtrust.py
|
||||
+++ b/ipaserver/install/plugins/adtrust.py
|
||||
@@ -705,7 +705,8 @@ class update_tdo_to_new_layout(Updater):
|
||||
self.set_krb_principal([tgt_principal, nbt_principal],
|
||||
passwd_incoming,
|
||||
t_dn,
|
||||
- flags=self.KRB_PRINC_CREATE_DEFAULT)
|
||||
+ flags=self.KRB_PRINC_CREATE_DEFAULT
|
||||
+ | self.KRB_PRINC_CREATE_AGENT_PERMISSION)
|
||||
|
||||
# 3. INBOUND: krbtgt/<OUR REALM>@<REMOTE REALM> must exist
|
||||
trust_principal = self.tgt_principal_template.format(
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -0,0 +1,543 @@
|
||||
From 9a8553fc297987b006b28ade2098d613f9b5360f Mon Sep 17 00:00:00 2001
|
||||
From: Jay Gondaliya <jgondali@redhat.com>
|
||||
Date: Thu, 12 Mar 2026 19:03:24 +0530
|
||||
Subject: [PATCH] ipatests: Add user-principal ipa-getkeytab and
|
||||
ipa-rmkeytab cmdline tests
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Port bash acceptance tests (getkeytab_001–getkeytab_007, rmkeytab_001-rmkeytab_003) from downstream to cmdline tests in test_ipagetkeytab.py:
|
||||
|
||||
test_getkeytab_users:
|
||||
getkeytab_001: quiet mode, insufficient access, empty ccache
|
||||
getkeytab_002: --server / -s with valid and invalid hostnames, DNS-based server discovery when -s is omitted
|
||||
getkeytab_003: --principal / -p with unknown, invalid-realm,
|
||||
bare, and realm-qualified principals
|
||||
getkeytab_004: --keytab / -k creation, enctype content, invalid path
|
||||
getkeytab_005: -e single enctype filtering and kinit validation
|
||||
getkeytab_006: --password / -P key rotation
|
||||
getkeytab_007: -D / -w bind DN error cases (empty DN, wrong
|
||||
password, missing password)
|
||||
|
||||
test_rmkeytab_cmd:
|
||||
rmkeytab_001: removal by principal (valid and invalid)
|
||||
rmkeytab_002: removal by realm
|
||||
rmkeytab_003: invalid keytab path
|
||||
|
||||
Tests that duplicate existing coverage are skipped with comments referencing the original tests. Parametrized variants are used where flags accept both short and long forms.
|
||||
|
||||
Signed-off-by: Jay Gondaliya <jgondali@redhat.com>
|
||||
Fixes: https://pagure.io/freeipa/issue/9957
|
||||
Assisted-by: Claude <noreply@anthropic.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
---
|
||||
ipatests/test_cmdline/test_ipagetkeytab.py | 459 +++++++++++++++++++++
|
||||
1 file changed, 459 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_cmdline/test_ipagetkeytab.py b/ipatests/test_cmdline/test_ipagetkeytab.py
|
||||
index c4678a76d..9ec9efae6 100755
|
||||
--- a/ipatests/test_cmdline/test_ipagetkeytab.py
|
||||
+++ b/ipatests/test_cmdline/test_ipagetkeytab.py
|
||||
@@ -22,6 +22,7 @@ Test `ipa-getkeytab`
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
+import configparser
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
@@ -40,6 +41,28 @@ from ipatests.test_xmlrpc.tracker import host_plugin, service_plugin
|
||||
from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_digits, add_oc
|
||||
from contextlib import contextmanager
|
||||
|
||||
+from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker
|
||||
+from ipatests.util import unlock_principal_password
|
||||
+
|
||||
+GK_USER1 = 'gkuser1'
|
||||
+GK_USER2 = 'gkuser2'
|
||||
+GK_INIT_PW = 'TempSecret123!'
|
||||
+GK_USER_PW = 'Secret123'
|
||||
+
|
||||
+CRYPTO_POLICIES_KRB5 = '/etc/krb5.conf.d/crypto-policies'
|
||||
+
|
||||
+
|
||||
+def get_permitted_enctypes():
|
||||
+ """Read permitted encryption types from the system crypto-policies.
|
||||
+
|
||||
+ Returns a list of enctype name strings, e.g.
|
||||
+ ['aes256-cts-hmac-sha1-96', 'aes128-cts-hmac-sha1-96', ...].
|
||||
+ """
|
||||
+ cfg = configparser.ConfigParser()
|
||||
+ cfg.read(CRYPTO_POLICIES_KRB5)
|
||||
+ raw = cfg.get('libdefaults', 'permitted_enctypes', fallback='')
|
||||
+ return [e for e in raw.split() if e]
|
||||
+
|
||||
|
||||
@contextmanager
|
||||
def use_keytab(principal, keytab):
|
||||
@@ -63,6 +86,57 @@ def use_keytab(principal, keytab):
|
||||
setattr(context, 'principal', old_principal)
|
||||
|
||||
|
||||
+@contextmanager
|
||||
+def kinit_as_user(principal, password):
|
||||
+ """Kinit as *principal* into a private ccache; yield its path.
|
||||
+
|
||||
+ private_ccache() already sets KRB5CCNAME in os.environ, so callers
|
||||
+ (and ipautil.run) inherit it automatically.
|
||||
+ """
|
||||
+ with private_ccache() as ccache_path:
|
||||
+ ipautil.run(
|
||||
+ ['kinit', principal],
|
||||
+ stdin=password + '\n',
|
||||
+ raiseonerr=True,
|
||||
+ capture_output=True,
|
||||
+ capture_error=True,
|
||||
+ )
|
||||
+ yield ccache_path
|
||||
+
|
||||
+
|
||||
+def run_getkeytab(args, env=None, stdin=None):
|
||||
+ """Run ipa-getkeytab with arbitrary arguments."""
|
||||
+ return ipautil.run(
|
||||
+ [paths.IPA_GETKEYTAB] + list(args),
|
||||
+ raiseonerr=False,
|
||||
+ capture_output=True,
|
||||
+ capture_error=True,
|
||||
+ stdin=stdin,
|
||||
+ env=env,
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+def run_rmkeytab(args, env=None):
|
||||
+ """Run ipa-rmkeytab with arbitrary arguments."""
|
||||
+ return ipautil.run(
|
||||
+ [paths.IPA_RMKEYTAB] + list(args),
|
||||
+ raiseonerr=False,
|
||||
+ capture_output=True,
|
||||
+ capture_error=True,
|
||||
+ env=env,
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+def klist_keytab(keytab_path):
|
||||
+ """Run ``klist -ekt`` and return the result object."""
|
||||
+ return ipautil.run(
|
||||
+ ['klist', '-ekt', keytab_path],
|
||||
+ raiseonerr=False,
|
||||
+ capture_output=True,
|
||||
+ capture_error=True,
|
||||
+ )
|
||||
+
|
||||
+
|
||||
@pytest.fixture(scope='class')
|
||||
def test_host(request):
|
||||
host_tracker = host_plugin.HostTracker(u'test-host')
|
||||
@@ -76,6 +150,20 @@ def test_service(request, test_host, keytab_retrieval_setup):
|
||||
return service_tracker.make_fixture(request)
|
||||
|
||||
|
||||
+@pytest.fixture(scope='class')
|
||||
+def gk_users(request, keytab_retrieval_setup):
|
||||
+ """Create GK_USER1 and GK_USER2; delete them after the class."""
|
||||
+ for uid in (GK_USER1, GK_USER2):
|
||||
+ tracker = UserTracker(
|
||||
+ name=uid, givenname='Test', sn='GKUser',
|
||||
+ userpassword=GK_INIT_PW,
|
||||
+ )
|
||||
+ tracker.make_fixture(request)
|
||||
+ tracker.make_create_command()()
|
||||
+ tracker.exists = True
|
||||
+ unlock_principal_password(uid, GK_INIT_PW, GK_USER_PW)
|
||||
+
|
||||
+
|
||||
@pytest.mark.needs_ipaapi
|
||||
class KeytabRetrievalTest(cmdline_test):
|
||||
"""
|
||||
@@ -134,6 +222,22 @@ class KeytabRetrievalTest(cmdline_test):
|
||||
rc = result.returncode
|
||||
assert rc == retcode
|
||||
|
||||
+ def _get_user1_keytab(self, keytab_path=None):
|
||||
+ """Populate *keytab_path* (default self.keytabname) with GK_USER1's
|
||||
+ keytab."""
|
||||
+ keytab = keytab_path or self.keytabname
|
||||
+ run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', GK_USER1, '-k', keytab]
|
||||
+ )
|
||||
+
|
||||
+ def _assert_getkeytab_succeeds(self, result):
|
||||
+ """Assert ipa-getkeytab returned success with the expected message."""
|
||||
+ assert result.returncode == 0
|
||||
+ assert (
|
||||
+ 'Keytab successfully retrieved and stored in:'
|
||||
+ in result.error_output
|
||||
+ )
|
||||
+
|
||||
|
||||
@pytest.mark.tier0
|
||||
class test_ipagetkeytab(KeytabRetrievalTest):
|
||||
@@ -495,3 +599,358 @@ class test_smb_service(KeytabRetrievalTest):
|
||||
ipanthash = entry.single_value.get('ipanthash')
|
||||
conn.disconnect()
|
||||
assert ipanthash != b'MagicRegen', 'LDBM backend entry corruption'
|
||||
+
|
||||
+
|
||||
+# -----------------------------------------------------------------------
|
||||
+# User-principal tests ported from bash acceptance tests
|
||||
+# (t.ipa-get-rm-keytabs.sh: getkeytab_001 – getkeytab_006)
|
||||
+# -----------------------------------------------------------------------
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+@pytest.mark.usefixtures('gk_users')
|
||||
+class test_getkeytab_users(KeytabRetrievalTest):
|
||||
+ """
|
||||
+ User-principal ipa-getkeytab tests (bash getkeytab_001 – getkeytab_006).
|
||||
+ """
|
||||
+ command = paths.IPA_GETKEYTAB
|
||||
+
|
||||
+ def _ensure_keytab_absent(self, keytab_path=None):
|
||||
+ try:
|
||||
+ os.unlink(keytab_path or self.keytabname)
|
||||
+ except OSError:
|
||||
+ pass
|
||||
+
|
||||
+ # --- getkeytab_001: quiet mode, access rights, missing ccache ---
|
||||
+
|
||||
+ def test_insufficient_access_as_other_user(self):
|
||||
+ """Retrieving another user's keytab without admin rights fails."""
|
||||
+ principal1 = '{}@{}'.format(GK_USER1, api.env.realm)
|
||||
+ principal2 = '{}@{}'.format(GK_USER2, api.env.realm)
|
||||
+ with kinit_as_user(principal1, GK_USER_PW):
|
||||
+ result = run_getkeytab(
|
||||
+ ['-s', api.env.host,
|
||||
+ '-p', principal2,
|
||||
+ '-k', self.keytabname],
|
||||
+ )
|
||||
+ assert result.returncode == 9
|
||||
+ assert (
|
||||
+ 'Failed to parse result: Insufficient access rights'
|
||||
+ in result.error_output
|
||||
+ )
|
||||
+
|
||||
+ def test_empty_ccache_returns_exit6(self):
|
||||
+ """An empty ccache (simulating kdestroy) returns exit 6."""
|
||||
+ principal1 = '{}@{}'.format(GK_USER1, api.env.realm)
|
||||
+ with private_ccache():
|
||||
+ result = run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', principal1,
|
||||
+ '-k', self.keytabname],
|
||||
+ )
|
||||
+ assert result.returncode == 6
|
||||
+ assert (
|
||||
+ 'Kerberos User Principal not found. '
|
||||
+ 'Do you have a valid Credential Cache?'
|
||||
+ in result.error_output
|
||||
+ )
|
||||
+
|
||||
+ def test_quiet_suppresses_success_message(self):
|
||||
+ """-q must suppress the keytab-stored success message.
|
||||
+
|
||||
+ Note: test_6_quiet_mode checks -q returncode with a service
|
||||
+ principal but does not verify the message is absent.
|
||||
+ """
|
||||
+ result = run_getkeytab(
|
||||
+ ['-q', '-s', api.env.host, '-p', GK_USER1,
|
||||
+ '-k', self.keytabname],
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ assert result.error_output == '', (
|
||||
+ f"Expected no output with -q, got: {result.error_output!r}"
|
||||
+ )
|
||||
+
|
||||
+ # Skipped: test_normal_mode_shows_success_message
|
||||
+ # — covered by test_6_quiet_mode which verifies the success message
|
||||
+ # appears without -q.
|
||||
+
|
||||
+ # --- getkeytab_002: --server / -s ---
|
||||
+
|
||||
+ @pytest.mark.parametrize('flag', ['--server', '-s'])
|
||||
+ def test_invalid_server_fails(self, flag):
|
||||
+ """An invalid server hostname returns exit 9 with bind error."""
|
||||
+ result = run_getkeytab(
|
||||
+ [flag, 'invalid.ipaserver.com',
|
||||
+ '-p', GK_USER1,
|
||||
+ '-k', self.keytabname],
|
||||
+ )
|
||||
+ assert result.returncode == 9
|
||||
+ assert 'Failed to bind to server' in result.error_output
|
||||
+
|
||||
+ @pytest.mark.parametrize('flag', ['--server', '-s'])
|
||||
+ def test_valid_server_succeeds(self, flag):
|
||||
+ """A valid server hostname retrieves the keytab successfully.
|
||||
+
|
||||
+ Note: -s case is also covered by test_7_server_name_check
|
||||
+ with a service principal.
|
||||
+ """
|
||||
+ result = run_getkeytab(
|
||||
+ [flag, api.env.host, '-p', GK_USER1,
|
||||
+ '-k', self.keytabname],
|
||||
+ )
|
||||
+ self._assert_getkeytab_succeeds(result)
|
||||
+
|
||||
+ def test_dns_discovery_succeeds(self):
|
||||
+ """ipa-getkeytab discovers the server via DNS when -s is omitted."""
|
||||
+ result = run_getkeytab(
|
||||
+ ['-p', GK_USER1, '-k', self.keytabname],
|
||||
+ )
|
||||
+ self._assert_getkeytab_succeeds(result)
|
||||
+
|
||||
+ # --- getkeytab_003: --principal / -p ---
|
||||
+
|
||||
+ @pytest.mark.parametrize('flag', ['--principal', '-p'])
|
||||
+ @pytest.mark.parametrize('principal', [
|
||||
+ pytest.param('unknownuser', id='unknown'),
|
||||
+ pytest.param(
|
||||
+ '{}@INVALID.IPASERVER.REALM.COM'.format(GK_USER1),
|
||||
+ id='invalid-realm',
|
||||
+ ),
|
||||
+ ])
|
||||
+ def test_principal_not_found(self, flag, principal):
|
||||
+ """Unresolvable principals return exit 9 with not-found error.
|
||||
+
|
||||
+ Note: test_1_run tests a similar scenario with a non-existent
|
||||
+ service principal.
|
||||
+ """
|
||||
+ result = run_getkeytab(
|
||||
+ ['-s', api.env.host, flag, principal,
|
||||
+ '-k', self.keytabname],
|
||||
+ )
|
||||
+ assert result.returncode == 9
|
||||
+ assert (
|
||||
+ 'Failed to parse result: PrincipalName not found.'
|
||||
+ in result.error_output
|
||||
+ )
|
||||
+
|
||||
+ @pytest.mark.parametrize('flag', ['--principal', '-p'])
|
||||
+ @pytest.mark.parametrize('with_realm', [False, True],
|
||||
+ ids=['bare', 'with-realm'])
|
||||
+ def test_valid_principal_succeeds(self, flag, with_realm):
|
||||
+ """Both bare and realm-qualified principals retrieve the keytab."""
|
||||
+ principal = ('{}@{}'.format(GK_USER1, api.env.realm)
|
||||
+ if with_realm else GK_USER1)
|
||||
+ result = run_getkeytab(
|
||||
+ ['-s', api.env.host, flag, principal,
|
||||
+ '-k', self.keytabname],
|
||||
+ )
|
||||
+ self._assert_getkeytab_succeeds(result)
|
||||
+
|
||||
+ # --- getkeytab_004: --keytab / -k ---
|
||||
+
|
||||
+ @pytest.mark.parametrize('flag', ['--keytab', '-k'])
|
||||
+ def test_creates_keytab_when_absent(self, flag):
|
||||
+ """The keytab file is created when it does not previously exist."""
|
||||
+ self._ensure_keytab_absent()
|
||||
+ result = run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', GK_USER1, flag, self.keytabname],
|
||||
+ )
|
||||
+ self._assert_getkeytab_succeeds(result)
|
||||
+ assert os.path.isfile(self.keytabname)
|
||||
+
|
||||
+ @pytest.mark.parametrize('flag', ['--keytab', '-k'])
|
||||
+ def test_keytab_contains_aes_enctypes(self, flag):
|
||||
+ """The created keytab contains both aes256 and aes128 entries."""
|
||||
+ run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', GK_USER1, flag, self.keytabname]
|
||||
+ )
|
||||
+ klist_result = klist_keytab(self.keytabname)
|
||||
+ assert api.env.realm in klist_result.output
|
||||
+ assert 'aes128' in klist_result.output
|
||||
+ assert 'aes256' in klist_result.output
|
||||
+
|
||||
+ @pytest.mark.parametrize('flag', ['--keytab', '-k'])
|
||||
+ def test_text_file_path_returns_exit11(self, flag):
|
||||
+ """Writing to a pre-existing plain-text file returns exit 11."""
|
||||
+ txtfd, txt_path = tempfile.mkstemp(suffix='.txt')
|
||||
+ os.close(txtfd)
|
||||
+ try:
|
||||
+ result = run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', GK_USER1, flag, txt_path],
|
||||
+ )
|
||||
+ assert result.returncode == 11
|
||||
+ assert 'Failed to add key to the keytab' in result.error_output
|
||||
+ finally:
|
||||
+ try:
|
||||
+ os.unlink(txt_path)
|
||||
+ except OSError:
|
||||
+ pass
|
||||
+
|
||||
+ # --- getkeytab_005: -e encryption types ---
|
||||
+
|
||||
+ def test_system_keytab_has_default_enctypes(self):
|
||||
+ """The system keytab must contain both aes256 and aes128."""
|
||||
+ klist_result = klist_keytab('/etc/krb5.keytab')
|
||||
+ assert '(aes256-cts-hmac-sha1-96)' in klist_result.output
|
||||
+ assert '(aes128-cts-hmac-sha1-96)' in klist_result.output
|
||||
+
|
||||
+ @pytest.mark.parametrize('enctype', get_permitted_enctypes())
|
||||
+ def test_single_enctype(self, enctype):
|
||||
+ """With -e <enctype> only that enctype appears and kinit works.
|
||||
+
|
||||
+ Note: test_8_keytab_encryption_check tests -e with multiple
|
||||
+ enctypes but only asserts success, not klist content.
|
||||
+ """
|
||||
+ expected = f"({enctype})"
|
||||
+ self._ensure_keytab_absent()
|
||||
+ run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', GK_USER1,
|
||||
+ '-k', self.keytabname, '-e', enctype],
|
||||
+ )
|
||||
+ klist_result = klist_keytab(self.keytabname)
|
||||
+ assert expected in klist_result.output
|
||||
+ for enc in get_permitted_enctypes():
|
||||
+ if enc != enctype:
|
||||
+ assert f"({enc})" not in klist_result.output
|
||||
+ assert '(des3-cbc-sha1)' not in klist_result.output
|
||||
+ assert '(arcfour-hmac)' not in klist_result.output
|
||||
+ with private_ccache():
|
||||
+ ipautil.run(
|
||||
+ ['kinit', '-k', '-t', self.keytabname,
|
||||
+ '{}@{}'.format(GK_USER1, api.env.realm)],
|
||||
+ raiseonerr=True,
|
||||
+ capture_output=True,
|
||||
+ capture_error=True,
|
||||
+ )
|
||||
+
|
||||
+ def test_invalid_enctype_returns_exit8(self):
|
||||
+ """An invalid -e value returns exit 8 and creates no keytab."""
|
||||
+ self._ensure_keytab_absent()
|
||||
+ result = run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', GK_USER1,
|
||||
+ '-k', self.keytabname, '-e', 'invalid'],
|
||||
+ )
|
||||
+ assert result.returncode == 8
|
||||
+ assert 'Warning unrecognized encryption type' in result.error_output
|
||||
+ assert not os.path.isfile(self.keytabname)
|
||||
+
|
||||
+ # --- getkeytab_006: --password / -P ---
|
||||
+
|
||||
+ @pytest.mark.parametrize('flag', ['--password', '-P'])
|
||||
+ def test_password_flag_rotates_keys(self, flag):
|
||||
+ """After --password and a random-key reset, the original password
|
||||
+ will no longer authenticate.
|
||||
+
|
||||
+ Sequence: admin sets password-derived keys -> user kinits -> user
|
||||
+ regenerates random keys -> kinit with the original password must fail.
|
||||
+ """
|
||||
+ self._ensure_keytab_absent()
|
||||
+ principal1 = '{}@{}'.format(GK_USER1, api.env.realm)
|
||||
+ stdin = '{pw}\n{pw}\n'.format(pw=GK_USER_PW)
|
||||
+ result = run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', GK_USER1,
|
||||
+ '-k', self.keytabname, flag],
|
||||
+ stdin=stdin,
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ with kinit_as_user(principal1, GK_USER_PW):
|
||||
+ regen = run_getkeytab(
|
||||
+ ['-s', api.env.host, '-p', GK_USER1,
|
||||
+ '-k', self.keytabname],
|
||||
+ )
|
||||
+ assert regen.returncode == 0
|
||||
+ with private_ccache():
|
||||
+ kinit_result = ipautil.run(
|
||||
+ ['kinit', principal1],
|
||||
+ stdin=GK_USER_PW + '\n',
|
||||
+ raiseonerr=False,
|
||||
+ capture_output=True,
|
||||
+ capture_error=True,
|
||||
+ )
|
||||
+ assert kinit_result.returncode != 0
|
||||
+ assert (
|
||||
+ 'Password incorrect' in kinit_result.error_output
|
||||
+ or 'Preauthentication failed' in kinit_result.error_output
|
||||
+ )
|
||||
+
|
||||
+ # --- getkeytab_007: -D / -w bind DN error cases ---
|
||||
+
|
||||
+ # Skipped: test_no_ccache_returns_exit6
|
||||
+ # — identical to test_empty_ccache_returns_exit6 above.
|
||||
+ # Skipped: test_valid_dm_credentials_succeed
|
||||
+ # — covered by TestBindMethods.test_retrieval_with_dm_creds.
|
||||
+
|
||||
+ @pytest.mark.parametrize('extra_args,exitcode,message', [
|
||||
+ (['-D', ' ', '-w', GK_USER_PW],
|
||||
+ 9, 'Anonymous Binds are not allowed'),
|
||||
+ (['-D', 'cn=Directory Manager', '-w', ' '],
|
||||
+ 9, 'Simple bind failed'),
|
||||
+ (['-D', 'cn=Directory Manager'],
|
||||
+ 10, 'Bind password required when using a bind DN'),
|
||||
+ ], ids=['empty-dn', 'wrong-password', 'missing-password'])
|
||||
+ def test_binddn_error_cases(self, extra_args, exitcode, message):
|
||||
+ """Bind DN error cases return appropriate exit codes."""
|
||||
+ result = run_getkeytab(
|
||||
+ ['--server', 'localhost',
|
||||
+ '-p', GK_USER1,
|
||||
+ '-k', self.keytabname] + extra_args,
|
||||
+ )
|
||||
+ assert result.returncode == exitcode
|
||||
+ assert message in result.error_output
|
||||
+
|
||||
+
|
||||
+# -----------------------------------------------------------------------
|
||||
+# ipa-rmkeytab tests (bash rmkeytab_001 – rmkeytab_003)
|
||||
+# -----------------------------------------------------------------------
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+@pytest.mark.usefixtures('gk_users')
|
||||
+class test_rmkeytab_cmd(KeytabRetrievalTest):
|
||||
+ """
|
||||
+ ipa-rmkeytab tests (bash rmkeytab_001 – rmkeytab_003).
|
||||
+ """
|
||||
+ command = paths.IPA_RMKEYTAB
|
||||
+
|
||||
+ # --- rmkeytab_001: -p removes a named principal ---
|
||||
+
|
||||
+ def test_invalid_principal_returns_exit5(self):
|
||||
+ """A non-existent principal name returns exit 5."""
|
||||
+ self._get_user1_keytab()
|
||||
+ result = run_rmkeytab(['-p', 'invalidprinc', '-k', self.keytabname])
|
||||
+ assert result.returncode == 5
|
||||
+ assert 'principal not found' in result.error_output
|
||||
+
|
||||
+ def test_valid_principal_removed(self):
|
||||
+ """A present principal is removed and absent from klist."""
|
||||
+ self._get_user1_keytab()
|
||||
+ result = run_rmkeytab(['-p', GK_USER1, '-k', self.keytabname])
|
||||
+ assert result.returncode == 0
|
||||
+ assert (
|
||||
+ 'Removing principal {}'.format(GK_USER1)
|
||||
+ in result.error_output
|
||||
+ )
|
||||
+ assert GK_USER1 not in klist_keytab(self.keytabname).output
|
||||
+
|
||||
+ # --- rmkeytab_002: -r removes all principals of the given realm ---
|
||||
+
|
||||
+ def test_realm_removes_all_principals(self):
|
||||
+ """All principals of the realm are removed and absent from klist."""
|
||||
+ self._get_user1_keytab()
|
||||
+ result = run_rmkeytab(['-r', api.env.realm, '-k', self.keytabname])
|
||||
+ assert result.returncode == 0
|
||||
+ assert (
|
||||
+ 'Removing principal {}@{}'.format(GK_USER1, api.env.realm)
|
||||
+ in result.error_output
|
||||
+ )
|
||||
+ assert api.env.realm not in klist_keytab(self.keytabname).output
|
||||
+
|
||||
+ # --- rmkeytab_003: -k with a non-existent path ---
|
||||
+
|
||||
+ def test_invalid_keytab_path_returns_exit7(self):
|
||||
+ """A non-existent keytab path returns exit 7."""
|
||||
+ self._get_user1_keytab()
|
||||
+ result = run_rmkeytab(
|
||||
+ ['-p', GK_USER1, '-k', '/opt/invalid.keytab']
|
||||
+ )
|
||||
+ assert result.returncode == 7
|
||||
+ assert 'Failed to set cursor' in result.error_output
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
From 6a2310eda39b1341258211c7630ef4baf4555df5 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Mon, 9 Dec 2024 23:03:56 +0530
|
||||
Subject: [PATCH] Fix the typo in ipa_migrate_constants.
|
||||
|
||||
ipa-migrate.log displays Privileges migrated as Privledges
|
||||
due to typo in labelling i.e 'label': 'Privledges'
|
||||
Hence changed that to 'label': 'Privileges'
|
||||
|
||||
---- LOG FILE ----
|
||||
INFO - Privledges: 3
|
||||
------------------
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9715
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipaserver/install/ipa_migrate_constants.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
|
||||
index c140414ea6c607a93e35ef0705480d1002b7945e..e8192fb1aabae1c36669370eff242428a1f0355f 100644
|
||||
--- a/ipaserver/install/ipa_migrate_constants.py
|
||||
+++ b/ipaserver/install/ipa_migrate_constants.py
|
||||
@@ -886,7 +886,7 @@ DB_OBJECTS = {
|
||||
'pbac_priv': {
|
||||
'oc': ['groupofnames'],
|
||||
'subtree': ',cn=privileges,cn=pbac,$SUFFIX',
|
||||
- 'label': 'Privledges',
|
||||
+ 'label': 'Privileges',
|
||||
'mode': 'all',
|
||||
'count': 0,
|
||||
},
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -0,0 +1,285 @@
|
||||
From b6d50197882dd62b5416948cb232c77c0349c0d3 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Thu, 5 Mar 2026 19:29:26 +0530
|
||||
Subject: [PATCH] ipatests: Additional tests for ipa-ipa migration
|
||||
testsuite
|
||||
|
||||
Covers tests scenarios for sshpubkey.
|
||||
|
||||
1. Test to check sshpubkey is migrated for regular ipa user
|
||||
2. Test to check sshpubkey is migrated for staged user
|
||||
3. Test to check sshpubkey is migrated for preserved user
|
||||
4. Test to check idoverides is migrated.
|
||||
|
||||
Covers test scenarios for TestIPAMigrationDNSRecords
|
||||
|
||||
1. Test to check dns zone is migrated
|
||||
2. Test to check dynamic update value is preserved when migrated
|
||||
3. Test to check dnsforwardzone is migrated
|
||||
4. Test to check A record is migrated
|
||||
|
||||
Covers test scenarios for IPA Privieleges and Permissions
|
||||
|
||||
1. Test to check that IPA role and its privilege are migrated in Prod mode
|
||||
2. Test to check that PBAC Permissions are migrated in Prod mode
|
||||
3. Test to check that sysaccounts are migrated.
|
||||
4. Test to check selinuxusermap is migrated
|
||||
|
||||
Assisted-by: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Fixes: https://pagure.io/freeipa/issue/9964
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Sudhir Menon <sumenon@redhat.com>
|
||||
---
|
||||
.../test_ipa_ipa_migration.py | 164 +++++++++++++++++-
|
||||
1 file changed, 155 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
index 18f335de196a9d6c7d30e4adf35385122376b7d9..105c79b610804b68522e568b4028be5a2dc72714 100644
|
||||
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
@@ -201,6 +201,12 @@ def prepare_ipa_server(master):
|
||||
master.run_command(
|
||||
["ipa", "dnszone-mod", "example.test", "--dynamic-update=TRUE"]
|
||||
)
|
||||
+ master.run_command(
|
||||
+ [
|
||||
+ "ipa", "dnsrecord-add", "example.test", "migratetest",
|
||||
+ "--a-rec", "192.0.2.100",
|
||||
+ ]
|
||||
+ )
|
||||
|
||||
# Add hbac rule
|
||||
master.run_command(["ipa", "hbacrule-add", "--usercat=all", "test1"])
|
||||
@@ -221,7 +227,7 @@ def prepare_ipa_server(master):
|
||||
"dnsforwardzone-add",
|
||||
"forwardzone.test",
|
||||
"--forwarder",
|
||||
- "10.11.12.13",
|
||||
+ "192.168.124.10",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -293,6 +299,11 @@ def prepare_ipa_server(master):
|
||||
]
|
||||
)
|
||||
|
||||
+ # Add sysaccount
|
||||
+ master.run_command(
|
||||
+ ["ipa", "sysaccount-add", "migrate-test-sysaccount", "--random"]
|
||||
+ )
|
||||
+
|
||||
|
||||
def run_migrate(
|
||||
host, mode, remote_host, bind_dn=None, bind_pwd=None, extra_args=None
|
||||
@@ -664,15 +675,15 @@ class TestIPAMigrateCLIOptions(MigrationTest):
|
||||
realm_name = self.master.domain.realm
|
||||
base_dn = str(self.master.domain.basedn)
|
||||
dse_ldif = textwrap.dedent(
|
||||
- f"""
|
||||
+ """
|
||||
dn: cn={realm_name},cn=kerberos,{base_dn}
|
||||
cn: {realm_name}
|
||||
objectClass: top
|
||||
objectClass: krbrealmcontainer
|
||||
"""
|
||||
).format(
|
||||
- realm_name=self.master.domain.realm,
|
||||
- base_dn=str(self.master.domain.basedn),
|
||||
+ realm_name=realm_name,
|
||||
+ base_dn=base_dn,
|
||||
)
|
||||
self.replicas[0].put_file_contents(ldif_file_path, dse_ldif)
|
||||
result = run_migrate(
|
||||
@@ -731,7 +742,6 @@ class TestIPAMigrateCLIOptions(MigrationTest):
|
||||
base_dn = str(self.master.domain.basedn)
|
||||
subtree = 'cn=security,{}'.format(base_dn)
|
||||
params = ['-s', subtree, '-n', '-x']
|
||||
- base_dn = str(self.master.domain.basedn)
|
||||
CUSTOM_SUBTREE_LOG = (
|
||||
"Add db entry 'cn=security,{} - custom'"
|
||||
).format(base_dn)
|
||||
@@ -853,7 +863,7 @@ class TestIPAMigrateCLIOptions(MigrationTest):
|
||||
def test_ipa_migrate_stage_mode_with_cert(self):
|
||||
"""
|
||||
This testcase checks that ipa-migrate command
|
||||
- works without the 'ValuerError'
|
||||
+ works without the 'ValueError'
|
||||
when -Z <cert> option is used with valid cert
|
||||
"""
|
||||
cert_file = '/tmp/ipa.crt'
|
||||
@@ -878,7 +888,7 @@ class TestIPAMigrateCLIOptions(MigrationTest):
|
||||
error when invalid cert is specified with
|
||||
-Z option
|
||||
"""
|
||||
- cert_file = '/tmp/invaid_cert.crt'
|
||||
+ cert_file = '/tmp/invalid_cert.crt'
|
||||
invalid_cert = (
|
||||
b'-----BEGIN CERTIFICATE-----\n'
|
||||
b'MIIFazCCDQYJKoZIhvcNAQELBQAw\n'
|
||||
@@ -1026,6 +1036,59 @@ class TestIPAMigrationProdMode(MigrationTest):
|
||||
assert 'Rule name: readfiles\n' in cmd1.stdout_text
|
||||
assert 'Sudo Command: /usr/bin/less\n' in cmd2.stdout_text
|
||||
|
||||
+ def test_ipa_migrate_prod_mode_roles_privileges(self):
|
||||
+ """
|
||||
+ Test that IPA roles are migrated from remote to local server
|
||||
+ """
|
||||
+ role_name = "junioradmin"
|
||||
+ privilege_name = "User Administrators"
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "role-show", role_name]
|
||||
+ )
|
||||
+ assert "Role name: {}".format(role_name) in result.stdout_text
|
||||
+ assert "Privileges: {}".format(privilege_name) in result.stdout_text
|
||||
+
|
||||
+ def test_ipa_migrate_prod_mode_permission(self):
|
||||
+ """
|
||||
+ Test that PBAC permission is migrated
|
||||
+ """
|
||||
+ permission_name = "Add Users"
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "permission-show", permission_name]
|
||||
+ )
|
||||
+ assert (f"Permission name: {permission_name}" in
|
||||
+ result.stdout_text)
|
||||
+
|
||||
+ def test_ipa_migrate_prod_mode_sysaccounts(self):
|
||||
+ """
|
||||
+ Test that system accounts (sysaccounts) are migrated
|
||||
+ from remote server to local server in prod mode.
|
||||
+ """
|
||||
+ sysaccount_name = "migrate-test-sysaccount"
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "sysaccount-show", sysaccount_name]
|
||||
+ )
|
||||
+ assert sysaccount_name in result.stdout_text
|
||||
+ assert (f"System account ID: {sysaccount_name}" in
|
||||
+ result.stdout_text)
|
||||
+
|
||||
+ def test_ipa_migrate_prod_mode_selinuxusermap(self):
|
||||
+ """
|
||||
+ Test that SELinux usermap is migrated from remote
|
||||
+ to local server.
|
||||
+ """
|
||||
+ usermap_name = "test1"
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "selinuxusermap-show", usermap_name]
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ assert f"Rule name: {usermap_name}" in result.stdout_text
|
||||
+ assert "xguest_u:s0" in result.stdout_text
|
||||
+
|
||||
def test_ipa_migrate_prod_mode_new_user_sid(self):
|
||||
"""
|
||||
This testcase checks that in prod-mode uid/gid of the
|
||||
@@ -1188,6 +1251,89 @@ class TestIPAMigrationProdMode(MigrationTest):
|
||||
assert DEBUG_LOG in install_msg
|
||||
|
||||
|
||||
+class TestIPAMigrationDNSRecords(MigrationTest):
|
||||
+ """
|
||||
+ Tests to verify all DNS zones, forward zones, and DNS records
|
||||
+ are migrated when ipa-migrate is run with --migrate-dns (-B).
|
||||
+ "By default all DNS entries are migrated" / -B to migrate DNS.
|
||||
+ """
|
||||
+ num_replicas = 1
|
||||
+ num_clients = 1
|
||||
+ topology = "line"
|
||||
+
|
||||
+ @pytest.fixture(autouse=True)
|
||||
+ def run_migration_with_dns(self):
|
||||
+ """
|
||||
+ Run full prod-mode migration with -B so that DNS is migrated
|
||||
+ to the local server. All tests in this class assume DNS has
|
||||
+ been migrated.
|
||||
+ """
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.kinit_admin(self.replicas[0])
|
||||
+ run_migrate(
|
||||
+ self.replicas[0],
|
||||
+ "prod-mode",
|
||||
+ self.master.hostname,
|
||||
+ "cn=Directory Manager",
|
||||
+ self.master.config.admin_password,
|
||||
+ extra_args=["-B", "-n"],
|
||||
+ )
|
||||
+
|
||||
+ def test_dns_zone_example_test_migrated(self):
|
||||
+ """
|
||||
+ Check that DNS zone example.test (from prepare_ipa_server)
|
||||
+ is migrated to the local server.
|
||||
+ """
|
||||
+ zone_name = "example.test"
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "dnszone-show", zone_name]
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ assert "Zone name: {}".format(zone_name) in result.stdout_text
|
||||
+
|
||||
+ def test_dns_zone_dynamic_update_preserved(self):
|
||||
+ """
|
||||
+ Check that zone attribute dynamic update is preserved
|
||||
+ (prepare_ipa_server sets dynamic-update=TRUE for example.test).
|
||||
+ """
|
||||
+ zone_name = "example.test"
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "dnszone-show", zone_name]
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ assert "Dynamic update: True" in result.stdout_text
|
||||
+
|
||||
+ def test_dns_zone_has_system_records(self):
|
||||
+ """
|
||||
+ Check that migrated zone has system records (NS/SOA).
|
||||
+ """
|
||||
+ zone_name = "example.test"
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "dnsrecord-find", zone_name]
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ # Zone should have records (e.g. NS, SOA, or record list)
|
||||
+ assert (
|
||||
+ "NS record" in result.stdout_text
|
||||
+ or "SOA record" in result.stdout_text
|
||||
+ or "Record name" in result.stdout_text
|
||||
+ )
|
||||
+
|
||||
+ def test_dns_record_a_migrated(self):
|
||||
+ """
|
||||
+ Verify that the A record added in prepare_ipa_server is
|
||||
+ migrated to the local server.
|
||||
+ """
|
||||
+ zone_name = "example.test"
|
||||
+ record_name = "migratetest"
|
||||
+ record_value = "192.0.2.100"
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa", "dnsrecord-show", zone_name, record_name]
|
||||
+ )
|
||||
+ assert record_name in result.stdout_text
|
||||
+ assert record_value in result.stdout_text
|
||||
+
|
||||
+
|
||||
class TestIPAMigrationWithADtrust(IntegrationTest):
|
||||
"""
|
||||
Test for ipa-migrate tool with IPA Master having trust setup
|
||||
@@ -1319,9 +1465,9 @@ class TestIPAMigratewithBackupRestore(IntegrationTest):
|
||||
DB_LDIF_FILE = '{}-userRoot.ldif'.format(
|
||||
dashed_domain_name
|
||||
)
|
||||
- SCHEMA_LDIF_FILE = '{}''/config_files/schema/99user.ldif'.format(
|
||||
+ SCHEMA_LDIF_FILE = "{}/config_files/schema/99user.ldif".format(
|
||||
dashed_domain_name)
|
||||
- CONFIG_LDIF_FILE = '{}''/config_files/dse.ldif'.format(
|
||||
+ CONFIG_LDIF_FILE = "{}/config_files/dse.ldif".format(
|
||||
dashed_domain_name)
|
||||
param = [
|
||||
'-n', '-g', CONFIG_LDIF_FILE, '-m', SCHEMA_LDIF_FILE,
|
||||
--
|
||||
2.52.0
|
||||
|
||||
288
SOURCES/0038-ipatests-ipa-migrate-ds-test-scenarios.patch
Normal file
288
SOURCES/0038-ipatests-ipa-migrate-ds-test-scenarios.patch
Normal file
@ -0,0 +1,288 @@
|
||||
From 8f2bbb02ae64f322fb34f073d7a7cdb00a69aea0 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Thu, 19 Mar 2026 17:12:12 +0530
|
||||
Subject: [PATCH] ipatests: ipa-migrate-ds test scenarios
|
||||
|
||||
This patch tests ipa-migrated-ds related sceanrios
|
||||
|
||||
1. 389-ds instance is setup on client system.
|
||||
2. Attached sample instance1.ldif file in data/ds_migration folder.
|
||||
3. Attempt ipa-migrate-ds with configuration disabled.
|
||||
3. Attempt ipa-migrate-ds over ldaps.
|
||||
|
||||
Assisted by: claude-4.6-opus-high <noreply@anthropic.com>
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipatests/setup.py | 2 +-
|
||||
.../data/ds_migration/instance1.ldif | 48 +++++
|
||||
.../test_integration/test_ds_migration.py | 189 ++++++++++++++++++
|
||||
3 files changed, 238 insertions(+), 1 deletion(-)
|
||||
create mode 100644 ipatests/test_integration/data/ds_migration/instance1.ldif
|
||||
create mode 100644 ipatests/test_integration/test_ds_migration.py
|
||||
|
||||
diff --git a/ipatests/setup.py b/ipatests/setup.py
|
||||
index 0aec4a70d..07595a501 100644
|
||||
--- a/ipatests/setup.py
|
||||
+++ b/ipatests/setup.py
|
||||
@@ -56,7 +56,7 @@ if __name__ == '__main__':
|
||||
'ipatests': ['prci_definitions/*'],
|
||||
'ipatests.test_custodia': ['*.conf', 'empty.conf.d/*.conf'],
|
||||
'ipatests.test_install': ['*.update'],
|
||||
- 'ipatests.test_integration': ['scripts/*'],
|
||||
+ 'ipatests.test_integration': ['data/*/*.ldif'],
|
||||
'ipatests.test_ipaclient': ['data/*/*/*'],
|
||||
'ipatests.test_ipalib': ['data/*'],
|
||||
'ipatests.test_ipaplatform': ['data/*'],
|
||||
diff --git a/ipatests/test_integration/data/ds_migration/instance1.ldif b/ipatests/test_integration/data/ds_migration/instance1.ldif
|
||||
new file mode 100644
|
||||
index 000000000..a148b52c8
|
||||
--- /dev/null
|
||||
+++ b/ipatests/test_integration/data/ds_migration/instance1.ldif
|
||||
@@ -0,0 +1,48 @@
|
||||
+# Minimal DS migration test data: base OUs and sample user/group
|
||||
+# Used when setting up 389-ds on the client for migrate-ds tests.
|
||||
+# Base suffix dc=testrealm,dc=test is created by dscreate.
|
||||
+
|
||||
+dn: dc=testrealm,dc=test
|
||||
+objectClass: top
|
||||
+objectClass: domain
|
||||
+objectClass: dcObject
|
||||
+dc: testrealm
|
||||
+
|
||||
+dn: ou=People,dc=testrealm,dc=test
|
||||
+objectClass: top
|
||||
+objectClass: organizationalUnit
|
||||
+ou: People
|
||||
+
|
||||
+dn: ou=Groups,dc=testrealm,dc=test
|
||||
+objectClass: top
|
||||
+objectClass: organizationalUnit
|
||||
+ou: Groups
|
||||
+
|
||||
+dn: cn=Directory Administrators, ou=Groups, dc=testrealm,dc=test
|
||||
+cn: Directory Administrators
|
||||
+objectclass: top
|
||||
+objectclass: groupofuniquenames
|
||||
+ou: Groups
|
||||
+uniquemember: uid=ldapuser_0001, ou=People, dc=testrealm,dc=test
|
||||
+
|
||||
+dn: uid=ldapuser_0001,ou=People,dc=testrealm,dc=test
|
||||
+objectClass: top
|
||||
+objectClass: person
|
||||
+objectClass: posixAccount
|
||||
+uid: ldapuser_0001
|
||||
+cn: LDAP User 1
|
||||
+sn: User 1
|
||||
+uidNumber: 1001
|
||||
+gidNumber: 1001
|
||||
+homeDirectory: /home/ldapuser_0001
|
||||
+userPassword: fo0m4nchU
|
||||
+telephonenumber: +1 123 444 555
|
||||
+
|
||||
+dn: cn=ldapgroup_0001,ou=Groups,dc=testrealm,dc=test
|
||||
+objectClass: top
|
||||
+objectClass: groupOfNames
|
||||
+objectClass: posixGroup
|
||||
+cn: ldapgroup_0001
|
||||
+gidNumber: 1001
|
||||
+member: uid=ldapuser_0001,ou=People,dc=testrealm,dc=test
|
||||
+
|
||||
diff --git a/ipatests/test_integration/test_ds_migration.py b/ipatests/test_integration/test_ds_migration.py
|
||||
new file mode 100644
|
||||
index 000000000..f16759b37
|
||||
--- /dev/null
|
||||
+++ b/ipatests/test_integration/test_ds_migration.py
|
||||
@@ -0,0 +1,189 @@
|
||||
+#
|
||||
+# Copyright (C) 2026 FreeIPA Contributors see COPYING for license
|
||||
+#
|
||||
+
|
||||
+"""
|
||||
+ipa-migrate-ds migration acceptance tests.
|
||||
+"""
|
||||
+
|
||||
+from __future__ import absolute_import
|
||||
+
|
||||
+import os
|
||||
+import textwrap
|
||||
+
|
||||
+from ipatests.test_integration.base import IntegrationTest
|
||||
+from ipatests.pytest_ipa.integration import tasks
|
||||
+
|
||||
+# 389-ds instance name and base DN on the client
|
||||
+DS_INSTANCE_NAME = "dsinstance_01"
|
||||
+DS_BASEDN = "dc=testrealm,dc=test"
|
||||
+DS_PORT = 389
|
||||
+DS_SECURE_PORT = 636
|
||||
+
|
||||
+
|
||||
+def _setup_389ds_on_client(client, admin_password):
|
||||
+ """
|
||||
+ Install 389 Directory Server on the client and load migration
|
||||
+ test data from instance1.ldif i.e (ou=People, ou=groups,
|
||||
+ sample user/group).
|
||||
+ """
|
||||
+ tasks.install_packages(client, ["389-ds-base"])
|
||||
+
|
||||
+ # Create instance via dscreate
|
||||
+ inf_content = textwrap.dedent("""\
|
||||
+ [general]
|
||||
+ full_machine_name = {hostname}
|
||||
+ [slapd]
|
||||
+ instance_name = {instance}
|
||||
+ port = {port}
|
||||
+ secure_port = {secure_port}
|
||||
+ root_dn = cn=Directory Manager
|
||||
+ root_password = {password}
|
||||
+ [backend-userroot]
|
||||
+ sample_entries = no
|
||||
+ suffix = {basedn}
|
||||
+ """).format(
|
||||
+ hostname=client.hostname,
|
||||
+ instance=DS_INSTANCE_NAME,
|
||||
+ port=DS_PORT,
|
||||
+ secure_port=DS_SECURE_PORT,
|
||||
+ password=admin_password,
|
||||
+ basedn=DS_BASEDN,
|
||||
+ )
|
||||
+ client.put_file_contents("/tmp/ds-instance.inf", inf_content)
|
||||
+ client.run_command(
|
||||
+ ["dscreate", "from-file", "/tmp/ds-instance.inf"]
|
||||
+ )
|
||||
+
|
||||
+ # Load migration test data from instance1.ldif
|
||||
+ test_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
+ ldif_path = os.path.join(
|
||||
+ test_dir, "data", "ds_migration", "instance1.ldif"
|
||||
+ )
|
||||
+ with open(ldif_path) as f:
|
||||
+ ldif_content = f.read()
|
||||
+ client.put_file_contents("/tmp/instance1.ldif", ldif_content)
|
||||
+ client.run_command(
|
||||
+ [
|
||||
+ "/usr/bin/ldapmodify",
|
||||
+ "-a", "-x", "-H", "ldap://localhost:{}".format(DS_PORT),
|
||||
+ "-D", "cn=Directory Manager", "-w", admin_password,
|
||||
+ "-f", "/tmp/instance1.ldif",
|
||||
+ ]
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+class TestDSMigrationConfig(IntegrationTest):
|
||||
+ """
|
||||
+ Test ipa migrate-ds related scenarios.
|
||||
+
|
||||
+ Uses a client host with 389-ds populated from instance1.ldif
|
||||
+ (ou=People, ou=groups, dc=testrealm,dc=test) for migration tests.
|
||||
+ """
|
||||
+
|
||||
+ topology = "line"
|
||||
+ num_replicas = 0
|
||||
+ num_clients = 1
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ # Install master and IPA client (full topology)
|
||||
+ super(TestDSMigrationConfig, cls).install(mh)
|
||||
+ # On the client host, set up 389-ds with migration test data
|
||||
+ _setup_389ds_on_client(
|
||||
+ cls.clients[0],
|
||||
+ cls.master.config.admin_password,
|
||||
+ )
|
||||
+
|
||||
+ def test_attempt_migration_with_configuration_false(self):
|
||||
+ """
|
||||
+ Test attempts ipa-migrate-ds with migration disabled.
|
||||
+ """
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ # Ensure migration is disabled
|
||||
+ cmd = ["ipa", "config-mod", "--enable-migration", "FALSE"]
|
||||
+ error_msg = "ipa: ERROR: no modifications to be performed"
|
||||
+ result = self.master.run_command(cmd, raiseonerr=False)
|
||||
+ assert result.returncode != 0
|
||||
+ tasks.assert_error(result, error_msg)
|
||||
+ client_host = self.clients[0].hostname
|
||||
+ ldap_uri = "ldap://{}:{}".format(client_host, DS_PORT)
|
||||
+ result = self.master.run_command(
|
||||
+ [
|
||||
+ "ipa",
|
||||
+ "migrate-ds",
|
||||
+ "--user-container=ou=People",
|
||||
+ "--group-container=ou=groups",
|
||||
+ ldap_uri,
|
||||
+ ],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+ assert (
|
||||
+ result.returncode != 0
|
||||
+ ), "migrate-ds should fail when migration is disabled"
|
||||
+ assert "migration mode is disabled" in (
|
||||
+ result.stdout_text + result.stderr_text
|
||||
+ ).lower()
|
||||
+
|
||||
+ def test_migration_over_ldaps(self):
|
||||
+ """
|
||||
+ Migrate from the client's 389-ds over LDAPS (port 636).
|
||||
+ """
|
||||
+ ca_cert_file = "/etc/ipa/remoteds.crt"
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ self.master.run_command(
|
||||
+ ["ipa", "config-mod", "--enable-migration", "TRUE"],
|
||||
+ )
|
||||
+
|
||||
+ # Copy 389-ds CA cert from client to master for LDAPS verification
|
||||
+ client = self.clients[0]
|
||||
+ ds_cert_dir = "/etc/dirsrv/slapd-{}".format(DS_INSTANCE_NAME)
|
||||
+ cert_result = client.run_command(
|
||||
+ [
|
||||
+ "certutil", "-d", ds_cert_dir, "-L", "-n",
|
||||
+ "Self-Signed-CA", "-a",
|
||||
+ ],
|
||||
+ )
|
||||
+ self.master.put_file_contents(
|
||||
+ ca_cert_file, cert_result.stdout_text
|
||||
+ )
|
||||
+ self.master.run_command(
|
||||
+ ["restorecon", ca_cert_file], raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ client_host = client.hostname
|
||||
+ ldaps_uri = "ldaps://{}:{}".format(client_host, DS_SECURE_PORT)
|
||||
+ user_container = "ou=People,{}".format(DS_BASEDN)
|
||||
+ group_container = "ou=groups,{}".format(DS_BASEDN)
|
||||
+
|
||||
+ self.master.run_command(
|
||||
+ [
|
||||
+ "ipa",
|
||||
+ "migrate-ds",
|
||||
+ "--with-compat",
|
||||
+ "--user-container",
|
||||
+ user_container,
|
||||
+ "--group-container",
|
||||
+ group_container,
|
||||
+ ldaps_uri,
|
||||
+ "--ca-cert-file",
|
||||
+ ca_cert_file,
|
||||
+ ],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ )
|
||||
+ # Verify migrated user and group from instance1.ldif
|
||||
+ self.master.run_command(
|
||||
+ ["ipa", "user-show", "ldapuser_0001"]
|
||||
+ )
|
||||
+ self.master.run_command(
|
||||
+ ["ipa", "group-show", "ldapgroup_0001"]
|
||||
+ )
|
||||
+
|
||||
+ # Clean up migrated user and group
|
||||
+ self.master.run_command(
|
||||
+ ["ipa", "user-del", "ldapuser_0001"], raiseonerr=False
|
||||
+ )
|
||||
+ self.master.run_command(
|
||||
+ ["ipa", "group-del", "ldapgroup_0001"], raiseonerr=False
|
||||
+ )
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
From d8d4c5507bd3784d323e6836ff8456ba9608f115 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Fri, 20 Sep 2024 09:36:41 +0200
|
||||
Subject: [PATCH] test_ipahealthcheck: skip connectivity_and_data check
|
||||
|
||||
PKI removed the clones.check connectivity_and_data check in
|
||||
11.5 and master branches. Skip the test depending on PKI version.
|
||||
The most recent version on 11.5 is 11.5.4 and still contains the check,
|
||||
hence skipping if version >= 11.5.5.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9668
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_ipahealthcheck.py | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
index a3e10b04487f0a12fdde19a8d1971a09f7d79b63..cc51a5a6a62fbc50927fc2fc51f129a069e70b69 100644
|
||||
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
||||
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
@@ -1219,6 +1219,9 @@ class TestIpaHealthCheck(IntegrationTest):
|
||||
This testcase checks that when ClonesConnectivyAndDataCheck
|
||||
is run it doesn't display source not found error
|
||||
"""
|
||||
+ if (tasks.get_pki_version(
|
||||
+ self.master) >= tasks.parse_version('11.5.5')):
|
||||
+ raise pytest.skip("PKI dropped ClonesConnectivyAndDataCheck")
|
||||
error_msg = (
|
||||
"Source 'pki.server.healthcheck.clones.connectivity_and_data' "
|
||||
"not found"
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -0,0 +1,506 @@
|
||||
From 51e669d46f912d39841bc40560682f9f414f68fe Mon Sep 17 00:00:00 2001
|
||||
From: Jay Gondaliya <jgondali@redhat.com>
|
||||
Date: Mon, 16 Mar 2026 15:03:17 +0530
|
||||
Subject: [PATCH] ipatests: Add ipa selfservice-show and
|
||||
selfservice-mod CLI tests to xmlrpc
|
||||
|
||||
Add two new Declarative test classes to test_selfservice_plugin.py covering the selfservice-show and selfservice-mod CLI command options, converted from the legacy bash test suite (selfservice_show_1001-1003 and selfservice_mod_1002-1008).
|
||||
|
||||
test_selfservice_show_cli covers:
|
||||
- selfservice-show --all
|
||||
- selfservice-show --all --raw
|
||||
- selfservice-show --raw
|
||||
|
||||
test_selfservice_mod_cli covers:
|
||||
- selfservice-mod with invalid attrs (negative)
|
||||
- selfservice-mod with invalid permissions (negative)
|
||||
- selfservice-mod with no changes / EmptyModlist (negative)
|
||||
- selfservice-mod with bad attrs must not delete the entry (BZ 747741)
|
||||
- selfservice-mod changing and expanding attrs (positive)
|
||||
- selfservice-mod with invalid/same permissions (negative)
|
||||
- selfservice-mod changing and expanding permissions (positive)
|
||||
|
||||
Tests for selfservice_mod_1001 and selfservice_mod_1009, which require interactive input and are not valid in a non-interactive context, are covered as integration tests in test_commands.py by test_selfservice_mod_no_attrs_or_permissions_all and test_selfservice_mod_no_attrs_or_permissions_raw. Tests for BZ 772675 (mod --raw) and BZ 747741 (bad attrs) cross-reference existing coverage in test_selfservice_misc.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9945
|
||||
Assisted-by: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Jay Gondaliya <jgondali@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_commands.py | 54 +++
|
||||
.../test_xmlrpc/test_selfservice_plugin.py | 396 ++++++++++++++++++
|
||||
2 files changed, 450 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 6d7ee8f2d..5021747b6 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -391,6 +391,60 @@ class TestIPACommand(IntegrationTest):
|
||||
assert result.returncode == 1
|
||||
assert "Number of permissions added 0" in result.stdout_text
|
||||
|
||||
+ def test_selfservice_mod_no_attrs_or_permissions_all(self):
|
||||
+ """selfservice-mod --all with no --attrs or --permissions fails.
|
||||
+
|
||||
+ When selfservice-mod is invoked with only --all and neither --attrs
|
||||
+ nor --permissions is supplied, the CLI has no modifications to apply
|
||||
+ and must return a non-zero exit code with an appropriate error message.
|
||||
+ """
|
||||
+ entry = 'test_selfservice_mod_all'
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ self.master.run_command(
|
||||
+ ['ipa', 'selfservice-add', entry,
|
||||
+ '--attrs=l', '--permissions=write']
|
||||
+ )
|
||||
+ try:
|
||||
+ result = self.master.run_command(
|
||||
+ ['ipa', 'selfservice-mod', entry, '--all'],
|
||||
+ stdin_text='\n',
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+ assert result.returncode != 0
|
||||
+ assert 'no modifications to be performed' in result.stderr_text
|
||||
+ finally:
|
||||
+ self.master.run_command(
|
||||
+ ['ipa', 'selfservice-del', entry],
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+
|
||||
+ def test_selfservice_mod_no_attrs_or_permissions_raw(self):
|
||||
+ """selfservice-mod --raw with no --attrs or --permissions fails.
|
||||
+
|
||||
+ When selfservice-mod is invoked with only --raw and neither --attrs
|
||||
+ nor --permissions is supplied, the CLI has no modifications to apply
|
||||
+ and must return a non-zero exit code with an appropriate error message.
|
||||
+ """
|
||||
+ entry = 'test_selfservice_mod_raw'
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ self.master.run_command(
|
||||
+ ['ipa', 'selfservice-add', entry,
|
||||
+ '--attrs=l', '--permissions=write']
|
||||
+ )
|
||||
+ try:
|
||||
+ result = self.master.run_command(
|
||||
+ ['ipa', 'selfservice-mod', entry, '--raw'],
|
||||
+ stdin_text='\n',
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+ assert result.returncode != 0
|
||||
+ assert 'no modifications to be performed' in result.stderr_text
|
||||
+ finally:
|
||||
+ self.master.run_command(
|
||||
+ ['ipa', 'selfservice-del', entry],
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+
|
||||
def test_change_sysaccount_password_issue7561(self):
|
||||
sysuser = 'system'
|
||||
original_passwd = 'Secret123'
|
||||
diff --git a/ipatests/test_xmlrpc/test_selfservice_plugin.py b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
index 8f2307a20..48dfd7cc3 100644
|
||||
--- a/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
@@ -942,3 +942,399 @@ class test_selfservice_cli_add_del(Declarative):
|
||||
),
|
||||
|
||||
]
|
||||
+
|
||||
+
|
||||
+# selfservice-show & selfservice-mod CLI test rule names
|
||||
+
|
||||
+SS_CLI_SHOW = 'SELFSERVICE_SHOW_TEST'
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+class test_selfservice_show_cli(Declarative):
|
||||
+ """Test selfservice-show CLI options."""
|
||||
+
|
||||
+ cleanup_commands = [
|
||||
+ ('selfservice_del', [SS_CLI_SHOW], {}),
|
||||
+ ]
|
||||
+
|
||||
+ tests = [
|
||||
+ dict(
|
||||
+ desc='Create %r for show tests' % SS_CLI_SHOW,
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ [SS_CLI_SHOW],
|
||||
+ dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions='write',
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_SHOW,
|
||||
+ summary='Added selfservice "%s"' % SS_CLI_SHOW,
|
||||
+ result=dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_SHOW,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Show with --all (positive test)
|
||||
+ dict(
|
||||
+ desc='Show %r with --all' % SS_CLI_SHOW,
|
||||
+ command=(
|
||||
+ 'selfservice_show',
|
||||
+ [SS_CLI_SHOW],
|
||||
+ {'all': True},
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_SHOW,
|
||||
+ summary=None,
|
||||
+ result=dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_SHOW,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Show with --all and --raw (positive test)
|
||||
+ dict(
|
||||
+ desc='Show %r with --all and --raw' % SS_CLI_SHOW,
|
||||
+ command=(
|
||||
+ 'selfservice_show',
|
||||
+ [SS_CLI_SHOW],
|
||||
+ {'all': True, 'raw': True},
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_SHOW,
|
||||
+ summary=None,
|
||||
+ result={
|
||||
+ 'aci': '(targetattr = "l")'
|
||||
+ '(version 3.0;acl '
|
||||
+ '"selfservice:%s";'
|
||||
+ 'allow (write) '
|
||||
+ 'userdn = "ldap:///self";)'
|
||||
+ % SS_CLI_SHOW,
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Show with --raw (positive test)
|
||||
+ dict(
|
||||
+ desc='Show %r with --raw' % SS_CLI_SHOW,
|
||||
+ command=(
|
||||
+ 'selfservice_show',
|
||||
+ [SS_CLI_SHOW],
|
||||
+ {'raw': True},
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_SHOW,
|
||||
+ summary=None,
|
||||
+ result={
|
||||
+ 'aci': '(targetattr = "l")'
|
||||
+ '(version 3.0;acl '
|
||||
+ '"selfservice:%s";'
|
||||
+ 'allow (write) '
|
||||
+ 'userdn = "ldap:///self";)'
|
||||
+ % SS_CLI_SHOW,
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete %r' % SS_CLI_SHOW,
|
||||
+ command=('selfservice_del', [SS_CLI_SHOW], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=SS_CLI_SHOW,
|
||||
+ summary='Deleted selfservice "%s"' % SS_CLI_SHOW,
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ ]
|
||||
+
|
||||
+
|
||||
+SS_CLI_MOD = 'SELFSERVICE_MOD_TEST'
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+class test_selfservice_mod_cli(Declarative):
|
||||
+ """Test selfservice-mod CLI options."""
|
||||
+
|
||||
+ cleanup_commands = [
|
||||
+ ('selfservice_del', [SS_CLI_MOD], {}),
|
||||
+ ]
|
||||
+
|
||||
+ tests = [
|
||||
+ dict(
|
||||
+ desc='Create %r for mod tests' % SS_CLI_MOD,
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions='write',
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_MOD,
|
||||
+ summary='Added selfservice "%s"' % SS_CLI_MOD,
|
||||
+ result=dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # test_selfservice_mod_no_attrs_or_permissions_all and
|
||||
+ # test_selfservice_mod_no_attrs_or_permissions_raw are covered in
|
||||
+ # integration tests in test_commands.py since they are
|
||||
+ # interactive tests.
|
||||
+
|
||||
+ # Modify with --all --attrs=badattr --permissions=write --raw
|
||||
+ # (negative test - invalid attr value)
|
||||
+ dict(
|
||||
+ desc='Try to modify %r with invalid attrs' % SS_CLI_MOD,
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(
|
||||
+ attrs=['badattr'],
|
||||
+ permissions='write',
|
||||
+ all=True,
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=errors.InvalidSyntax(
|
||||
+ attr=(
|
||||
+ r'targetattr "badattr" does not exist in schema. '
|
||||
+ r'Please add attributeTypes "badattr" to '
|
||||
+ r'schema if necessary. '
|
||||
+ r'ACL Syntax Error(-5):'
|
||||
+ r'(targetattr = \22badattr\22)'
|
||||
+ r'(version 3.0;acl '
|
||||
+ r'\22selfservice:%s\22;'
|
||||
+ r'allow (write) userdn = \22ldap:///self\22;)'
|
||||
+ ) % SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with --all --attrs=l --permissions=badperm --raw
|
||||
+ # (negative test - invalid permission value)
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Try to modify %r with invalid permissions'
|
||||
+ % SS_CLI_MOD
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions='badperm',
|
||||
+ all=True,
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=errors.ValidationError(
|
||||
+ name='permissions',
|
||||
+ error='"badperm" is not a valid permission',
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with same attrs and perms already set
|
||||
+ # (negative test - no modifications error)
|
||||
+ dict(
|
||||
+ desc='Try to modify %r with no changes' % SS_CLI_MOD,
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions='write',
|
||||
+ all=True,
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=errors.EmptyModlist(),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with --attrs=badattrs (negative test,
|
||||
+ # BZ 747741 - a bad attrs mod must not delete the entry).
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Try to modify %r with bad attrs (BZ 747741)'
|
||||
+ % SS_CLI_MOD
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(attrs=['badattrs']),
|
||||
+ ),
|
||||
+ expected=errors.InvalidSyntax(
|
||||
+ attr=(
|
||||
+ r'targetattr "badattrs" does not exist in schema. '
|
||||
+ r'Please add attributeTypes "badattrs" to '
|
||||
+ r'schema if necessary. '
|
||||
+ r'ACL Syntax Error(-5):'
|
||||
+ r'(targetattr = \22badattrs\22)'
|
||||
+ r'(version 3.0;acl '
|
||||
+ r'\22selfservice:%s\22;'
|
||||
+ r'allow (write) userdn = \22ldap:///self\22;)'
|
||||
+ ) % SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Verify entry still exists after failed mod (BZ 747741)
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Verify %r still exists after failed mod'
|
||||
+ % SS_CLI_MOD
|
||||
+ ),
|
||||
+ command=('selfservice_show', [SS_CLI_MOD], {}),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_MOD,
|
||||
+ summary=None,
|
||||
+ result=dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with --attrs=mobile (positive test - change attr)
|
||||
+ dict(
|
||||
+ desc='Modify %r attrs to mobile' % SS_CLI_MOD,
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(attrs=['mobile']),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_MOD,
|
||||
+ summary='Modified selfservice "%s"' % SS_CLI_MOD,
|
||||
+ result=dict(
|
||||
+ attrs=['mobile'],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with --attrs={mobile,l} (positive test - add attr)
|
||||
+ dict(
|
||||
+ desc='Modify %r attrs to mobile and l' % SS_CLI_MOD,
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(attrs=['mobile', 'l']),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_MOD,
|
||||
+ summary='Modified selfservice "%s"' % SS_CLI_MOD,
|
||||
+ result=dict(
|
||||
+ attrs=['mobile', 'l'],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with --permissions=badperm
|
||||
+ # (negative test - invalid permission string)
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Try to modify %r with invalid permissions'
|
||||
+ % SS_CLI_MOD
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(permissions='badperm'),
|
||||
+ ),
|
||||
+ expected=errors.ValidationError(
|
||||
+ name='permissions',
|
||||
+ error='"badperm" is not a valid permission',
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with --permissions=write
|
||||
+ # (negative test - same perm already set, no modification)
|
||||
+ dict(
|
||||
+ desc='Try to modify %r with same permissions' % SS_CLI_MOD,
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(permissions='write'),
|
||||
+ ),
|
||||
+ expected=errors.EmptyModlist(),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with --permissions=read (positive test - change perm)
|
||||
+ dict(
|
||||
+ desc='Modify %r permissions to read' % SS_CLI_MOD,
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(permissions='read'),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_MOD,
|
||||
+ summary='Modified selfservice "%s"' % SS_CLI_MOD,
|
||||
+ result=dict(
|
||||
+ attrs=['mobile', 'l'],
|
||||
+ permissions=['read'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Modify with --permissions={read,write} (positive test - add perm)
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Modify %r permissions to read and write'
|
||||
+ % SS_CLI_MOD
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_mod',
|
||||
+ [SS_CLI_MOD],
|
||||
+ dict(permissions=['read', 'write']),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_MOD,
|
||||
+ summary='Modified selfservice "%s"' % SS_CLI_MOD,
|
||||
+ result=dict(
|
||||
+ attrs=['mobile', 'l'],
|
||||
+ permissions=['read', 'write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # test_selfservice_mod_no_attrs_or_permissions_all and
|
||||
+ # test_selfservice_mod_no_attrs_or_permissions_raw are covered in
|
||||
+ # integration tests in test_commands.py since they are
|
||||
+ # interactive tests.
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete %r' % SS_CLI_MOD,
|
||||
+ command=('selfservice_del', [SS_CLI_MOD], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=SS_CLI_MOD,
|
||||
+ summary='Deleted selfservice "%s"' % SS_CLI_MOD,
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ ]
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
From c294b7f6dbcae9c01d5366b19b3b070ffb730fa6 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Wed, 11 Dec 2024 15:12:48 +0530
|
||||
Subject: [PATCH] ipatests: Fixes for ipa-ipa-migration tool
|
||||
|
||||
The test test_ipa_migrate_with_invalid_host has been
|
||||
failing in downstream run due to mismatch in the expected test output,
|
||||
hence the assert statement has been modified.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/3656
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_ipa_ipa_migration.py | 4 +---
|
||||
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
index 0c637a0141d95f34f951c60a9648adf8e87eaa63..95c29234fc7893d3eae5d900a58aa7b1162ed61d 100644
|
||||
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
@@ -437,10 +437,8 @@ class TestIPAMigrateCLIOptions(MigrationTest):
|
||||
"""
|
||||
hostname = "server.invalid.host"
|
||||
ERR_MSG = (
|
||||
- "IPA to IPA migration starting ...\n"
|
||||
"Failed to bind to remote server: cannot connect to "
|
||||
- "'ldap://"
|
||||
- "{}': \n".format(hostname)
|
||||
+ "'ldap://{}':".format(hostname)
|
||||
)
|
||||
result = run_migrate(
|
||||
self.replicas[0],
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
From ff5bb485d709ea02422cbafba51d23db384822af Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Fri, 20 Dec 2024 15:45:01 +0100
|
||||
Subject: [PATCH] Installation test: KRA on replica after cert renewal
|
||||
|
||||
Add a new test installing the KRA on a replica after the
|
||||
KRA certs have been renewed on the master.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9692
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_installation.py | 10 +++++++++-
|
||||
1 file changed, 9 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py
|
||||
index c5565c452010f23f038ddf329454b591ef09f6af..1fae472673a3934c86af2f9adc7b343c70e25b2b 100644
|
||||
--- a/ipatests/test_integration/test_installation.py
|
||||
+++ b/ipatests/test_integration/test_installation.py
|
||||
@@ -1322,7 +1322,7 @@ class TestInstallMaster(IntegrationTest):
|
||||
|
||||
class TestInstallMasterKRA(IntegrationTest):
|
||||
|
||||
- num_replicas = 0
|
||||
+ num_replicas = 1
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
@@ -1379,6 +1379,14 @@ class TestInstallMasterKRA(IntegrationTest):
|
||||
)
|
||||
assert starting_serial != int(cert.serial_number)
|
||||
|
||||
+ def test_install_replica_after_kracert_renewal(self):
|
||||
+ """
|
||||
+ Test replica installation with CA after the KRA certs renewal
|
||||
+ """
|
||||
+ tasks.install_replica(self.master, self.replicas[0],
|
||||
+ setup_ca=True)
|
||||
+ tasks.install_kra(self.replicas[0])
|
||||
+
|
||||
|
||||
class TestInstallMasterDNS(IntegrationTest):
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -0,0 +1,506 @@
|
||||
From c294159878de52fa5762025ee4be893f280c5320 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 31 Mar 2026 15:38:36 +0200
|
||||
Subject: [PATCH] ipatests: Add selfservice-find cli tests to xmlrpc
|
||||
Add a new Declarative test class test_selfservice_cli_find covering CLI-level
|
||||
behaviour of the selfservice-find command:
|
||||
|
||||
- find with --all succeeds and returns the matching rule
|
||||
- non-existent or wrong attrs filter returns zero results
|
||||
- bad --name filter returns zero results with valid and invalid
|
||||
positional arg
|
||||
- bad permissions filter with --all --raw returns zero results,
|
||||
no internal error (BZ 747693)
|
||||
- all valid params with --all --raw succeeds and returns the raw
|
||||
ACI string (BZ 747693)
|
||||
- wrong or non-existent attrs filter returns zero results with and
|
||||
without name arg
|
||||
- valid --attrs filter succeeds with positional name arg and with
|
||||
--name option
|
||||
- valid --name filter succeeds and returns the matching rule
|
||||
- bad permissions filter returns zero results
|
||||
- valid --permissions filter succeeds with positional name arg and
|
||||
with --name option
|
||||
- --raw only succeeds and returns the raw ACI string, no internal
|
||||
error (BZ 747693)
|
||||
|
||||
Signed-off-by: Jay Gondaliya <jgondali@redhat.com>
|
||||
Fixes: https://pagure.io/freeipa/issue/9945
|
||||
Assisted-by: Claude <noreply@anthropic.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
.../test_xmlrpc/test_selfservice_plugin.py | 456 ++++++++++++++++++
|
||||
1 file changed, 456 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_xmlrpc/test_selfservice_plugin.py b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
index 48dfd7cc3..9e921156a 100644
|
||||
--- a/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_selfservice_plugin.py
|
||||
@@ -944,6 +944,462 @@ class test_selfservice_cli_add_del(Declarative):
|
||||
]
|
||||
|
||||
|
||||
+# selfservice-find CLI test rule name
|
||||
+SS_CLI_FIND = 'SELFSERVICE_FIND_TEST'
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+class test_selfservice_cli_find(Declarative):
|
||||
+ """Tests for the selfservice-find CLI command."""
|
||||
+
|
||||
+ cleanup_commands = [
|
||||
+ ('selfservice_del', [SS_CLI_FIND], {}),
|
||||
+ ]
|
||||
+
|
||||
+ tests = [
|
||||
+
|
||||
+ # Setup: create the rule used by all find tests
|
||||
+ dict(
|
||||
+ desc='Setup: create %r' % SS_CLI_FIND,
|
||||
+ command=(
|
||||
+ 'selfservice_add',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(attrs=['l'], permissions='write'),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=SS_CLI_FIND,
|
||||
+ summary='Added selfservice "%s"' % SS_CLI_FIND,
|
||||
+ result=dict(
|
||||
+ attrs=['l'],
|
||||
+ permissions=['write'],
|
||||
+ selfaci=True,
|
||||
+ aciname=SS_CLI_FIND,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Find with --all returns the parsed result
|
||||
+ dict(
|
||||
+ desc='Search for %r with --all' % SS_CLI_FIND,
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(all=True),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary='1 selfservice matched',
|
||||
+ result=[{
|
||||
+ 'attrs': ['l'],
|
||||
+ 'permissions': ['write'],
|
||||
+ 'selfaci': True,
|
||||
+ 'aciname': SS_CLI_FIND,
|
||||
+ }],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Bad attrs filter -- aci_find does pure string
|
||||
+ # comparison; no schema validation in find.
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Non-existent attr with all filters'
|
||||
+ ' returns no match (--all --raw)'
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(
|
||||
+ all=True,
|
||||
+ attrs=['badattrs'],
|
||||
+ aciname=SS_CLI_FIND,
|
||||
+ permissions='write',
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Wrong attr for this rule (has 'l', not 'mobile')
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Wrong attr for rule with all filters'
|
||||
+ ' returns no match (--all --raw)'
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(
|
||||
+ all=True,
|
||||
+ attrs=['mobile'],
|
||||
+ aciname=SS_CLI_FIND,
|
||||
+ permissions='write',
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Bad --name filter with --all --raw
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Valid name arg with bad --name filter'
|
||||
+ ' returns no match (--all --raw)'
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(
|
||||
+ all=True,
|
||||
+ attrs=['l'],
|
||||
+ aciname='badname',
|
||||
+ permissions='write',
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Bad name arg also set to 'badname'
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Bad name arg with bad --name filter'
|
||||
+ ' returns no match (--all --raw)'
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ ['badname'],
|
||||
+ dict(
|
||||
+ all=True,
|
||||
+ attrs=['l'],
|
||||
+ aciname='badname',
|
||||
+ permissions='write',
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Bad permissions with --all --raw (BZ 747693)
|
||||
+ # selfservice-find --raw must not return "internal error".
|
||||
+ # aci_find treats permissions as a plain string filter (no
|
||||
+ # validation), so 'badperm' simply matches nothing.
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Bad permissions with --all --raw'
|
||||
+ ' returns no match (BZ 747693)'
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(
|
||||
+ all=True,
|
||||
+ attrs=['l'],
|
||||
+ aciname=SS_CLI_FIND,
|
||||
+ permissions='badperm',
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # All valid params with --all --raw (BZ 747693)
|
||||
+ # selfservice-find --raw must not return "internal error".
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'All valid params with --all --raw'
|
||||
+ ' returns raw ACI (BZ 747693)'
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(
|
||||
+ all=True,
|
||||
+ attrs=['l'],
|
||||
+ aciname=SS_CLI_FIND,
|
||||
+ permissions='write',
|
||||
+ raw=True,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary='1 selfservice matched',
|
||||
+ result=[{
|
||||
+ 'aci': (
|
||||
+ '(targetattr = "l")'
|
||||
+ '(version 3.0;acl "selfservice:%s";'
|
||||
+ 'allow (write) '
|
||||
+ 'userdn = "ldap:///self";)'
|
||||
+ % SS_CLI_FIND
|
||||
+ ),
|
||||
+ }],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Bad attrs filter without --all --raw
|
||||
+ dict(
|
||||
+ desc='Wrong attr filter returns no match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(attrs=['mobile']),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Non-existent attr in filter
|
||||
+ dict(
|
||||
+ desc='Non-existent attr filter returns no match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(attrs=['badattrs']),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Non-existent attr without name arg
|
||||
+ dict(
|
||||
+ desc='Non-existent attr without name arg returns no match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [],
|
||||
+ dict(attrs=['badattrs']),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Valid attrs filter
|
||||
+ dict(
|
||||
+ desc='Valid attrs filter with name arg returns match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(attrs=['l']),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary='1 selfservice matched',
|
||||
+ result=[{
|
||||
+ 'attrs': ['l'],
|
||||
+ 'permissions': ['write'],
|
||||
+ 'selfaci': True,
|
||||
+ 'aciname': SS_CLI_FIND,
|
||||
+ }],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Without positional name arg but with --name
|
||||
+ # filter to get a deterministic result.
|
||||
+ dict(
|
||||
+ desc='Valid attrs filter with --name option returns match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [],
|
||||
+ dict(attrs=['l'], aciname=SS_CLI_FIND),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary='1 selfservice matched',
|
||||
+ result=[{
|
||||
+ 'attrs': ['l'],
|
||||
+ 'permissions': ['write'],
|
||||
+ 'selfaci': True,
|
||||
+ 'aciname': SS_CLI_FIND,
|
||||
+ }],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Bad --name filter
|
||||
+ dict(
|
||||
+ desc='Valid name arg with bad --name filter returns no match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(aciname='badname'),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Bad name arg also set to 'badname'
|
||||
+ dict(
|
||||
+ desc='Bad name arg with bad --name filter returns no match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ ['badname'],
|
||||
+ dict(aciname='badname'),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Valid --name filter
|
||||
+ dict(
|
||||
+ desc='Valid --name filter returns match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(aciname=SS_CLI_FIND),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary='1 selfservice matched',
|
||||
+ result=[{
|
||||
+ 'attrs': ['l'],
|
||||
+ 'permissions': ['write'],
|
||||
+ 'selfaci': True,
|
||||
+ 'aciname': SS_CLI_FIND,
|
||||
+ }],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Bad permissions filter -- aci_find treats permissions
|
||||
+ # as a plain string filter; 'badperm' matches nothing.
|
||||
+ dict(
|
||||
+ desc='Bad permissions filter returns no match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(permissions='badperm'),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=0,
|
||||
+ truncated=False,
|
||||
+ summary='0 selfservices matched',
|
||||
+ result=[],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Valid permissions filter
|
||||
+ dict(
|
||||
+ desc='Valid permissions filter with name arg returns match',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(permissions='write'),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary='1 selfservice matched',
|
||||
+ result=[{
|
||||
+ 'attrs': ['l'],
|
||||
+ 'permissions': ['write'],
|
||||
+ 'selfaci': True,
|
||||
+ 'aciname': SS_CLI_FIND,
|
||||
+ }],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Without positional name arg but with --name
|
||||
+ # filter to get a deterministic result.
|
||||
+ dict(
|
||||
+ desc=(
|
||||
+ 'Valid permissions filter with --name'
|
||||
+ ' option returns match'
|
||||
+ ),
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [],
|
||||
+ dict(
|
||||
+ permissions='write',
|
||||
+ aciname=SS_CLI_FIND,
|
||||
+ ),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary='1 selfservice matched',
|
||||
+ result=[{
|
||||
+ 'attrs': ['l'],
|
||||
+ 'permissions': ['write'],
|
||||
+ 'selfaci': True,
|
||||
+ 'aciname': SS_CLI_FIND,
|
||||
+ }],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ # Raw output only (BZ 747693)
|
||||
+ # selfservice-find --raw must not return "internal error".
|
||||
+ dict(
|
||||
+ desc='Raw output returns ACI string without error (BZ 747693)',
|
||||
+ command=(
|
||||
+ 'selfservice_find',
|
||||
+ [SS_CLI_FIND],
|
||||
+ dict(raw=True),
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary='1 selfservice matched',
|
||||
+ result=[{
|
||||
+ 'aci': (
|
||||
+ '(targetattr = "l")'
|
||||
+ '(version 3.0;acl "selfservice:%s";'
|
||||
+ 'allow (write) '
|
||||
+ 'userdn = "ldap:///self";)'
|
||||
+ % SS_CLI_FIND
|
||||
+ ),
|
||||
+ }],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ ]
|
||||
+
|
||||
+
|
||||
# selfservice-show & selfservice-mod CLI test rule names
|
||||
|
||||
SS_CLI_SHOW = 'SELFSERVICE_SHOW_TEST'
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
From a707083b0987e6ffabb817fcc5e5138b4c755459 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Fri, 20 Dec 2024 17:01:56 +0100
|
||||
Subject: [PATCH] KRA cert renewal: update ca.connector.KRA.transportCert
|
||||
|
||||
After the KRA transport cert has been renewed, the value
|
||||
of ca.connector.KRA.transportCert must also be updated in
|
||||
/etc/pki/pki-tomcat/ca/CS.cfg.
|
||||
Otherwise replica installation with KRA fails.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9692
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/cainstance.py | 13 ++++++++-----
|
||||
1 file changed, 8 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index 5c2c9f8b981cf5d587865f7680e2b231eae655e2..e03a8c863e14782679e19c6887f5e220131e4234 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -1225,11 +1225,14 @@ class CAInstance(DogtagInstance):
|
||||
"""
|
||||
|
||||
# The cert directive to update per nickname
|
||||
- directives = {'auditSigningCert cert-pki-ca': 'ca.audit_signing.cert',
|
||||
- 'ocspSigningCert cert-pki-ca': 'ca.ocsp_signing.cert',
|
||||
- 'caSigningCert cert-pki-ca': 'ca.signing.cert',
|
||||
- 'subsystemCert cert-pki-ca': 'ca.subsystem.cert',
|
||||
- 'Server-Cert cert-pki-ca': 'ca.sslserver.cert'}
|
||||
+ directives = {
|
||||
+ 'auditSigningCert cert-pki-ca': 'ca.audit_signing.cert',
|
||||
+ 'ocspSigningCert cert-pki-ca': 'ca.ocsp_signing.cert',
|
||||
+ 'caSigningCert cert-pki-ca': 'ca.signing.cert',
|
||||
+ 'subsystemCert cert-pki-ca': 'ca.subsystem.cert',
|
||||
+ 'Server-Cert cert-pki-ca': 'ca.sslserver.cert',
|
||||
+ 'transportCert cert-pki-kra': 'ca.connector.KRA.transportCert'
|
||||
+ }
|
||||
|
||||
try:
|
||||
self.backup_config()
|
||||
--
|
||||
2.47.1
|
||||
|
||||
36
SOURCES/0041-ipatests-fix-the-method-add_a_record.patch
Normal file
36
SOURCES/0041-ipatests-fix-the-method-add_a_record.patch
Normal file
@ -0,0 +1,36 @@
|
||||
From 0306d2c5ebb1732fb33f3da4199f29ec887e1db8 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Thu, 9 Apr 2026 12:49:18 +0200
|
||||
Subject: [PATCH] ipatests: fix the method add_a_record
|
||||
|
||||
The method add_a_record first checks if a DNS record exists.
|
||||
If it finds one, it assumes there is already a DNS A record for
|
||||
the host but this assumption is wrong. The dnsrecord-show command
|
||||
may return another type of DNS record (for instance an SSHFP record).
|
||||
|
||||
Create the A record if dnsrecord-show fails or if it doesn't return
|
||||
any A record.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9972
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Anuja More <amore@redhat.com>
|
||||
---
|
||||
ipatests/pytest_ipa/integration/tasks.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index ee2befd6998da3b8035e4e015d7528b5a9676a7b..5ce18483d6c1049104afc1cef6524a7a19fc9047 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -1620,7 +1620,7 @@ def add_a_record(master, host):
|
||||
raiseonerr=False)
|
||||
|
||||
# If not, add it
|
||||
- if cmd.returncode != 0:
|
||||
+ if cmd.returncode != 0 or 'A record' not in cmd.stdout_text:
|
||||
master.run_command(['ipa',
|
||||
'dnsrecord-add',
|
||||
master.domain.name,
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
From 2506d5de5a9dd8ebe6efc777c2eb76461f5b57e2 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Mon, 6 Jan 2025 10:12:15 -0500
|
||||
Subject: [PATCH] Add 30-second timeout for certmonger request/start tracking
|
||||
|
||||
certmonger needs to validate that the PIN/password and/or token
|
||||
are valid and available. In the case of a very slow HSM this can
|
||||
take longer than the 5-second default timeout.
|
||||
|
||||
We saw an HSM that took 18 seconds to start tracking the CA signing
|
||||
certificate so default to 30 to be safe.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9725
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
ipalib/install/certmonger.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py
|
||||
index 7b22295152f752b6ab4de0f3525d48c541677aff..efc1ba4f42eab98df5fac51bafa3acc83ae91831 100644
|
||||
--- a/ipalib/install/certmonger.py
|
||||
+++ b/ipalib/install/certmonger.py
|
||||
@@ -477,7 +477,7 @@ def request_cert(
|
||||
request_parameters['cert-perms'] = perms[0]
|
||||
request_parameters['key-perms'] = perms[1]
|
||||
|
||||
- result = cm.obj_if.add_request(request_parameters)
|
||||
+ result = cm.obj_if.add_request(request_parameters, timeout=30)
|
||||
try:
|
||||
if result[0]:
|
||||
request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
|
||||
@@ -581,7 +581,7 @@ def start_tracking(
|
||||
if nss_user:
|
||||
params['nss-user'] = nss_user
|
||||
|
||||
- result = cm.obj_if.add_request(params)
|
||||
+ result = cm.obj_if.add_request(params, timeout=30)
|
||||
try:
|
||||
if result[0]:
|
||||
request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
From a1b1a45c45f521659ee25709141b6470599030d0 Mon Sep 17 00:00:00 2001
|
||||
From: David Hanina <dhanina@redhat.com>
|
||||
Date: Mon, 13 Apr 2026 16:23:24 +0200
|
||||
Subject: [PATCH] ipatests-Remove xfail for sssd/issues/7169
|
||||
|
||||
4 of the failing tests are now succeeding, removing the xfail.
|
||||
|
||||
Signed-off-by: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_trust.py | 18 +++---------------
|
||||
1 file changed, 3 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
||||
index 0cab277c910a6d35f35b57e3068ee6f38706af59..d7f25d658b24f3b261260e735d3679e81e35ed15 100644
|
||||
--- a/ipatests/test_integration/test_trust.py
|
||||
+++ b/ipatests/test_integration/test_trust.py
|
||||
@@ -1219,14 +1219,7 @@ 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')
|
||||
- and sssd_version < tasks.parse_version("2.12.0"))
|
||||
- with xfail_context(cond2,
|
||||
- 'https://github.com/SSSD/sssd/issues/5989 '
|
||||
- 'and 7169'):
|
||||
- assert "domain users@{0}".format(self.ad_domain) in test_group
|
||||
+ assert "domain users@{0}".format(self.ad_domain) in test_group
|
||||
|
||||
@pytest.mark.parametrize('type', ['hybrid', 'true', "false"])
|
||||
def test_nonposixuser_nondefault_primary_group(self, type):
|
||||
@@ -1347,10 +1340,5 @@ class TestPosixAutoPrivateGroup(BaseTestTrust):
|
||||
assert (uid == self.uid_override
|
||||
and gid == self.gid_override)
|
||||
result = self.clients[0].run_command(['id', posixuser])
|
||||
- sssd_version = tasks.get_sssd_version(self.clients[0])
|
||||
- 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(
|
||||
- self.ad_domain) in result.stdout_text
|
||||
+ assert "10047(testgroup@{0})".format(
|
||||
+ self.ad_domain) in result.stdout_text
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
From 86a6e9cef5a3fdea3cb84c39575c90481bfe1623 Mon Sep 17 00:00:00 2001
|
||||
From: David Hanina <dhanina@redhat.com>
|
||||
Date: Tue, 14 Apr 2026 14:24:13 +0200
|
||||
Subject: [PATCH] Fix ipa ca-show ipa --all not listing RSN version
|
||||
|
||||
Resolves: RHEL-168047
|
||||
|
||||
Signed-off-by: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipaserver/install/cainstance.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index 4738517..8e7512f 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -1681,7 +1681,7 @@ class CAInstance(DogtagInstance):
|
||||
api.env.basedn)
|
||||
entry_attrs = api.Backend.ldap2.get_entry(dn)
|
||||
version = entry_attrs.single_value.get(
|
||||
- "ipaCaRandomSerialNumberVersion", "0"
|
||||
+ "ipaCaRandomSerialNumberVersion", "-"
|
||||
)
|
||||
if str(version) == str(value):
|
||||
return
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,624 +0,0 @@
|
||||
From d26ce5cccc211f83b3cce3fc5e548b5cb955bb81 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Fri, 13 Dec 2024 13:42:36 +0200
|
||||
Subject: [PATCH] Unify use of option parsers
|
||||
|
||||
Do not use direct optparse references, instead import IPAOptionParser
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
install/tools/ipa-adtrust-install.in | 4 +---
|
||||
install/tools/ipa-managed-entries.in | 3 ++-
|
||||
ipaclient/install/ipa_client_automount.py | 4 ++--
|
||||
ipaclient/install/ipa_client_samba.py | 4 ++--
|
||||
ipalib/cli.py | 21 ++++++++++++---------
|
||||
ipalib/plugable.py | 8 ++++----
|
||||
ipapython/admintool.py | 3 +--
|
||||
ipapython/config.py | 18 +++++++++++-------
|
||||
ipapython/install/cli.py | 9 ++++-----
|
||||
ipaserver/install/ipa_acme_manage.py | 6 ++----
|
||||
ipaserver/install/ipa_backup.py | 5 ++---
|
||||
ipaserver/install/ipa_cacert_manage.py | 9 ++++-----
|
||||
ipaserver/install/ipa_kra_install.py | 5 ++---
|
||||
ipaserver/install/ipa_restore.py | 5 ++---
|
||||
ipaserver/install/ipa_server_certinstall.py | 7 +++----
|
||||
ipatests/i18n.py | 8 ++++----
|
||||
makeapi.in | 5 ++---
|
||||
17 files changed, 60 insertions(+), 64 deletions(-)
|
||||
|
||||
diff --git a/install/tools/ipa-adtrust-install.in b/install/tools/ipa-adtrust-install.in
|
||||
index cb2b78e504896b96cdc378fd879cc1f00c60904f..e7b0e369259da5d28d703558d9293ccfaf68f3ed 100644
|
||||
--- a/install/tools/ipa-adtrust-install.in
|
||||
+++ b/install/tools/ipa-adtrust-install.in
|
||||
@@ -29,8 +29,6 @@ import sys
|
||||
|
||||
import six
|
||||
|
||||
-from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module
|
||||
-
|
||||
from ipalib.install import sysrestore
|
||||
from ipaserver.install import adtrust, service
|
||||
from ipaserver.install.installutils import (
|
||||
@@ -41,7 +39,7 @@ from ipapython.admintool import ScriptError
|
||||
from ipapython import version
|
||||
from ipapython import ipautil
|
||||
from ipalib import api, errors, krb_utils
|
||||
-from ipapython.config import IPAOptionParser
|
||||
+from ipapython.config import IPAOptionParser, SUPPRESS_HELP
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
|
||||
diff --git a/install/tools/ipa-managed-entries.in b/install/tools/ipa-managed-entries.in
|
||||
index e9be41b7a34ed62e65ab40bf544a6e4cea7c598a..e3f121943eb3b18ca8f7f8bfeae7813cbc9bd753 100644
|
||||
--- a/install/tools/ipa-managed-entries.in
|
||||
+++ b/install/tools/ipa-managed-entries.in
|
||||
@@ -39,7 +39,8 @@ logger = logging.getLogger(os.path.basename(__file__))
|
||||
def parse_options():
|
||||
usage = "%prog [options] <status|enable|disable>\n"
|
||||
usage += "%prog [options]\n"
|
||||
- parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
|
||||
+ parser = config.IPAOptionParser(usage=usage,
|
||||
+ formatter=config.IPAFormatter())
|
||||
|
||||
parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
help="Display debugging information about the update(s)")
|
||||
diff --git a/ipaclient/install/ipa_client_automount.py b/ipaclient/install/ipa_client_automount.py
|
||||
index 4439932bd723c40c429dac2c08e97d326e414d24..9f49ff9edeee2648d2be1dea6b09806ba0b5e041 100644
|
||||
--- a/ipaclient/install/ipa_client_automount.py
|
||||
+++ b/ipaclient/install/ipa_client_automount.py
|
||||
@@ -34,7 +34,6 @@ import SSSDConfig
|
||||
|
||||
from six.moves.urllib.parse import urlsplit
|
||||
|
||||
-from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
from ipapython import ipachangeconf
|
||||
from ipaclient import discovery
|
||||
from ipaclient.install.client import (
|
||||
@@ -52,6 +51,7 @@ from ipaplatform.tasks import tasks
|
||||
from ipaplatform import services
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.admintool import ScriptError
|
||||
+from ipapython.config import IPAOptionParser
|
||||
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
@@ -59,7 +59,7 @@ logger = logging.getLogger(os.path.basename(__file__))
|
||||
|
||||
def parse_options():
|
||||
usage = "%prog [options]\n"
|
||||
- parser = OptionParser(usage=usage)
|
||||
+ parser = IPAOptionParser(usage=usage)
|
||||
parser.add_option("--server", dest="server", help="FQDN of IPA server")
|
||||
parser.add_option(
|
||||
"--location",
|
||||
diff --git a/ipaclient/install/ipa_client_samba.py b/ipaclient/install/ipa_client_samba.py
|
||||
index 81d670c34cae0f91a436a2a2d1e0d525057b6cc7..5c33abb4cabe47ff311b7d769fefa37991f7d453 100755
|
||||
--- a/ipaclient/install/ipa_client_samba.py
|
||||
+++ b/ipaclient/install/ipa_client_samba.py
|
||||
@@ -9,7 +9,6 @@ import logging
|
||||
import os
|
||||
import gssapi
|
||||
from urllib.parse import urlsplit
|
||||
-from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
from contextlib import contextmanager
|
||||
|
||||
from ipaclient import discovery
|
||||
@@ -31,6 +30,7 @@ from ipaplatform.constants import constants
|
||||
from ipaplatform import services
|
||||
from ipapython.admintool import ScriptError
|
||||
from samba import generate_random_password
|
||||
+from ipapython.config import IPAOptionParser
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
logger.setLevel(logging.DEBUG)
|
||||
@@ -68,7 +68,7 @@ def use_api_as_principal(principal, keytab):
|
||||
|
||||
def parse_options():
|
||||
usage = "%prog [options]\n"
|
||||
- parser = OptionParser(usage=usage)
|
||||
+ parser = IPAOptionParser(usage=usage)
|
||||
parser.add_option(
|
||||
"--server",
|
||||
dest="server",
|
||||
diff --git a/ipalib/cli.py b/ipalib/cli.py
|
||||
index d9c2ac16513101098c8f9a2258ae930b3b2cebf0..667b213fd4e4706e6eecd4d2b4737fb3fede4c93 100644
|
||||
--- a/ipalib/cli.py
|
||||
+++ b/ipalib/cli.py
|
||||
@@ -30,7 +30,6 @@ import textwrap
|
||||
import sys
|
||||
import getpass
|
||||
import code
|
||||
-import optparse # pylint: disable=deprecated-module
|
||||
import os
|
||||
import pprint
|
||||
import fcntl
|
||||
@@ -71,6 +70,8 @@ from ipalib.text import _
|
||||
from ipalib import api
|
||||
from ipapython.dnsutil import DNSName
|
||||
from ipapython.admintool import ScriptError
|
||||
+from ipapython.config import (IPAOptionParser, IPAFormatter,
|
||||
+ OptionGroup, make_option)
|
||||
|
||||
import datetime
|
||||
|
||||
@@ -1121,7 +1122,8 @@ class Collector:
|
||||
def __todict__(self):
|
||||
return dict(self.__options)
|
||||
|
||||
-class CLIOptionParserFormatter(optparse.IndentedHelpFormatter):
|
||||
+
|
||||
+class CLIOptionParserFormatter(IPAFormatter):
|
||||
def format_argument(self, name, help_string):
|
||||
result = []
|
||||
opt_width = self.help_position - self.current_indent - 2
|
||||
@@ -1141,7 +1143,8 @@ class CLIOptionParserFormatter(optparse.IndentedHelpFormatter):
|
||||
result.append("\n")
|
||||
return "".join(result)
|
||||
|
||||
-class CLIOptionParser(optparse.OptionParser):
|
||||
+
|
||||
+class CLIOptionParser(IPAOptionParser):
|
||||
"""
|
||||
This OptionParser subclass adds an ability to print positional
|
||||
arguments in CLI help. Custom formatter is used to format the argument
|
||||
@@ -1151,13 +1154,13 @@ class CLIOptionParser(optparse.OptionParser):
|
||||
self._arguments = []
|
||||
if 'formatter' not in kwargs:
|
||||
kwargs['formatter'] = CLIOptionParserFormatter()
|
||||
- optparse.OptionParser.__init__(self, *args, **kwargs)
|
||||
+ IPAOptionParser.__init__(self, *args, **kwargs)
|
||||
|
||||
def format_option_help(self, formatter=None):
|
||||
"""
|
||||
Prepend argument help to standard OptionParser's option help
|
||||
"""
|
||||
- option_help = optparse.OptionParser.format_option_help(self, formatter)
|
||||
+ option_help = IPAOptionParser.format_option_help(self, formatter)
|
||||
|
||||
if isinstance(formatter, CLIOptionParserFormatter):
|
||||
heading = unicode(_("Positional arguments"))
|
||||
@@ -1272,7 +1275,7 @@ class cli(backend.Executioner):
|
||||
"""Get or create an option group for the given name"""
|
||||
option_group = option_groups.get(group_name)
|
||||
if option_group is None:
|
||||
- option_group = optparse.OptionGroup(parser, group_name)
|
||||
+ option_group = OptionGroup(parser, group_name)
|
||||
parser.add_option_group(option_group)
|
||||
option_groups[group_name] = option_group
|
||||
return option_group
|
||||
@@ -1298,7 +1301,7 @@ class cli(backend.Executioner):
|
||||
option_names = ['--%s' % cli_name]
|
||||
if option.cli_short_name:
|
||||
option_names.append('-%s' % option.cli_short_name)
|
||||
- opt = optparse.make_option(*option_names, **kw)
|
||||
+ opt = make_option(*option_names, **kw)
|
||||
if option.option_group is None:
|
||||
parser.add_option(opt)
|
||||
else:
|
||||
@@ -1312,7 +1315,7 @@ class cli(backend.Executioner):
|
||||
group = _get_option_group(unicode(_('Deprecated options')))
|
||||
for alias in option.deprecated_cli_aliases:
|
||||
name = '--%s' % alias
|
||||
- group.add_option(optparse.make_option(name, **new_kw))
|
||||
+ group.add_option(make_option(name, **new_kw))
|
||||
|
||||
for arg in cmd.args():
|
||||
name = self.__get_arg_name(arg, format_name=False)
|
||||
@@ -1442,7 +1445,7 @@ class cli(backend.Executioner):
|
||||
)
|
||||
|
||||
|
||||
-class IPAHelpFormatter(optparse.IndentedHelpFormatter):
|
||||
+class IPAHelpFormatter(IPAFormatter):
|
||||
"""Formatter suitable for printing IPA command help
|
||||
|
||||
The default help formatter reflows text to fit the terminal, but it
|
||||
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
|
||||
index 2e2861df054fa7ed3533d0f82a23b65322ab591b..a87e6e8914fa5579920aad8190c7f6a86cb3dfea 100644
|
||||
--- a/ipalib/plugable.py
|
||||
+++ b/ipalib/plugable.py
|
||||
@@ -33,7 +33,6 @@ import sys
|
||||
import threading
|
||||
import os
|
||||
from os import path
|
||||
-import optparse # pylint: disable=deprecated-module
|
||||
import textwrap
|
||||
import collections
|
||||
import importlib
|
||||
@@ -47,6 +46,7 @@ from ipalib.util import classproperty
|
||||
from ipalib.base import ReadOnly, lock, islocked
|
||||
from ipalib.constants import DEFAULT_CONFIG
|
||||
from ipapython import ipa_log_manager, ipautil
|
||||
+from ipapython.config import IPAOptionParser, IPAFormatter
|
||||
from ipapython.ipa_log_manager import (
|
||||
LOGGING_FORMAT_FILE,
|
||||
LOGGING_FORMAT_STDERR)
|
||||
@@ -526,7 +526,7 @@ class API(ReadOnly):
|
||||
|
||||
def build_global_parser(self, parser=None, context=None):
|
||||
"""
|
||||
- Add global options to an optparse.OptionParser instance.
|
||||
+ Add global options to an IPAOptionParser instance.
|
||||
"""
|
||||
def config_file_callback(option, opt, value, parser):
|
||||
if not os.path.isfile(value):
|
||||
@@ -536,7 +536,7 @@ class API(ReadOnly):
|
||||
parser.values.conf = value
|
||||
|
||||
if parser is None:
|
||||
- parser = optparse.OptionParser(
|
||||
+ parser = IPAOptionParser(
|
||||
add_help_option=False,
|
||||
formatter=IPAHelpFormatter(),
|
||||
usage='%prog [global-options] COMMAND [command-options]',
|
||||
@@ -821,7 +821,7 @@ class API(ReadOnly):
|
||||
return self.__next[plugin]
|
||||
|
||||
|
||||
-class IPAHelpFormatter(optparse.IndentedHelpFormatter):
|
||||
+class IPAHelpFormatter(IPAFormatter):
|
||||
def format_epilog(self, epilog):
|
||||
text_width = self.width - self.current_indent
|
||||
indent = " " * self.current_indent
|
||||
diff --git a/ipapython/admintool.py b/ipapython/admintool.py
|
||||
index fdb4400d879165612405b3ba159a220773bd8d69..dff9112eba4009d2fc1a6202756c6671ba4d909d 100644
|
||||
--- a/ipapython/admintool.py
|
||||
+++ b/ipapython/admintool.py
|
||||
@@ -26,7 +26,6 @@ import logging
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
-from optparse import OptionGroup # pylint: disable=deprecated-module
|
||||
|
||||
from ipaplatform.osinfo import osinfo
|
||||
from ipapython import version
|
||||
@@ -113,7 +112,7 @@ class AdminTool:
|
||||
:param parser: The parser to add options to
|
||||
:param debug_option: Add a --debug option as an alias to --verbose
|
||||
"""
|
||||
- group = OptionGroup(parser, "Logging and output options")
|
||||
+ group = config.OptionGroup(parser, "Logging and output options")
|
||||
group.add_option("-v", "--verbose", dest="verbose", default=False,
|
||||
action="store_true", help="print debugging information")
|
||||
if debug_option:
|
||||
diff --git a/ipapython/config.py b/ipapython/config.py
|
||||
index f53d0f998aaedf47715764577dd7f5048f4ca841..7af4dfdeb0047586de9694f233be8bf543190b9c 100644
|
||||
--- a/ipapython/config.py
|
||||
+++ b/ipapython/config.py
|
||||
@@ -18,9 +18,9 @@
|
||||
#
|
||||
from __future__ import absolute_import
|
||||
|
||||
-# pylint: disable=deprecated-module
|
||||
-from optparse import (
|
||||
- Option, Values, OptionParser, IndentedHelpFormatter, OptionValueError)
|
||||
+# pylint: disable=deprecated-module, disable=unused-import
|
||||
+from optparse import (Option, Values, OptionGroup, OptionParser, SUPPRESS_HELP,
|
||||
+ IndentedHelpFormatter, OptionValueError, make_option)
|
||||
# pylint: enable=deprecated-module
|
||||
from copy import copy
|
||||
from configparser import ConfigParser as SafeConfigParser
|
||||
@@ -113,10 +113,14 @@ class IPAOptionParser(OptionParser):
|
||||
description=None,
|
||||
formatter=None,
|
||||
add_help_option=True,
|
||||
- prog=None):
|
||||
- OptionParser.__init__(self, usage, option_list, option_class,
|
||||
- version, conflict_handler, description,
|
||||
- formatter, add_help_option, prog)
|
||||
+ prog=None,
|
||||
+ epilog=None):
|
||||
+ OptionParser.__init__(self, usage=usage, option_list=option_list,
|
||||
+ option_class=option_class, version=version,
|
||||
+ conflict_handler=conflict_handler,
|
||||
+ description=description, formatter=formatter,
|
||||
+ add_help_option=add_help_option, prog=prog,
|
||||
+ epilog=epilog)
|
||||
|
||||
def get_safe_opts(self, opts):
|
||||
"""
|
||||
diff --git a/ipapython/install/cli.py b/ipapython/install/cli.py
|
||||
index ab212be4e2c3f9c2aae97f7759672ef7c68c3347..a048b3c7c64bc22338e4517d64b33a44446c58a3 100644
|
||||
--- a/ipapython/install/cli.py
|
||||
+++ b/ipapython/install/cli.py
|
||||
@@ -9,12 +9,11 @@ Command line support.
|
||||
import collections
|
||||
import enum
|
||||
import logging
|
||||
-import optparse # pylint: disable=deprecated-module
|
||||
import signal
|
||||
|
||||
import six
|
||||
|
||||
-from ipapython import admintool
|
||||
+from ipapython import admintool, config
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipapython.ipautil import (CheckedIPAddress, CheckedIPAddressLoopback,
|
||||
private_ccache)
|
||||
@@ -158,7 +157,7 @@ class ConfigureTool(admintool.AdminTool):
|
||||
try:
|
||||
opt_group = groups[group_cls]
|
||||
except KeyError:
|
||||
- opt_group = groups[group_cls] = optparse.OptionGroup(
|
||||
+ opt_group = groups[group_cls] = config.OptionGroup(
|
||||
parser, "{0} options".format(group_cls.description))
|
||||
parser.add_option_group(opt_group)
|
||||
|
||||
@@ -232,7 +231,7 @@ class ConfigureTool(admintool.AdminTool):
|
||||
if not hidden:
|
||||
help = knob_cls.description
|
||||
else:
|
||||
- help = optparse.SUPPRESS_HELP
|
||||
+ help = config.SUPPRESS_HELP
|
||||
|
||||
opt_group.add_option(
|
||||
*opt_strs,
|
||||
@@ -256,7 +255,7 @@ class ConfigureTool(admintool.AdminTool):
|
||||
|
||||
# fake option parser to parse positional arguments
|
||||
# (because optparse does not support positional argument parsing)
|
||||
- fake_option_parser = optparse.OptionParser()
|
||||
+ fake_option_parser = config.IPAOptionParser()
|
||||
self.add_options(fake_option_parser, True)
|
||||
|
||||
fake_option_map = {option.dest: option
|
||||
diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py
|
||||
index dc2359f49dfdd5c8f44ab96ee11a7240f8937e11..0decab394c1c18067fe0c194c040805a8d93d42d 100644
|
||||
--- a/ipaserver/install/ipa_acme_manage.py
|
||||
+++ b/ipaserver/install/ipa_acme_manage.py
|
||||
@@ -7,14 +7,12 @@ import enum
|
||||
import pki.util
|
||||
import logging
|
||||
|
||||
-from optparse import OptionGroup # pylint: disable=deprecated-module
|
||||
-
|
||||
from ipalib import api, errors, x509
|
||||
from ipalib import _
|
||||
from ipalib.facts import is_ipa_configured
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.admintool import AdminTool
|
||||
-from ipapython import cookie, dogtag
|
||||
+from ipapython import cookie, dogtag, config
|
||||
from ipapython.ipautil import run
|
||||
from ipapython.certdb import NSSDatabase, EXTERNAL_CA_TRUST_FLAGS
|
||||
from ipaserver.install import cainstance
|
||||
@@ -143,7 +141,7 @@ class IPAACMEManage(AdminTool):
|
||||
@classmethod
|
||||
def add_options(cls, parser):
|
||||
|
||||
- group = OptionGroup(parser, 'Pruning')
|
||||
+ group = config.OptionGroup(parser, 'Pruning')
|
||||
group.add_option(
|
||||
"--enable", dest="enable", action="store_true",
|
||||
default=False, help="Enable certificate pruning")
|
||||
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
|
||||
index 982e5dfc4c0339aada88f936ab450b7fc16944f2..b6af63813fc4eaadab44ad95386d86ae4f1e21ee 100644
|
||||
--- a/ipaserver/install/ipa_backup.py
|
||||
+++ b/ipaserver/install/ipa_backup.py
|
||||
@@ -20,7 +20,6 @@
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import logging
|
||||
-import optparse # pylint: disable=deprecated-module
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
@@ -32,7 +31,7 @@ import six
|
||||
from ipaplatform.paths import paths
|
||||
from ipaplatform import services
|
||||
from ipalib import api, errors
|
||||
-from ipapython import version
|
||||
+from ipapython import version, config
|
||||
from ipapython.ipautil import run, write_tmp_file
|
||||
from ipapython import admintool, certdb
|
||||
from ipapython.dn import DN
|
||||
@@ -245,7 +244,7 @@ class Backup(admintool.AdminTool):
|
||||
|
||||
parser.add_option(
|
||||
"--gpg-keyring", dest="gpg_keyring",
|
||||
- help=optparse.SUPPRESS_HELP)
|
||||
+ help=config.SUPPRESS_HELP)
|
||||
parser.add_option(
|
||||
"--gpg", dest="gpg", action="store_true",
|
||||
default=False, help="Encrypt the backup")
|
||||
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
|
||||
index f6ab736fa985b00ba66a7001c4c4e2188841bcbe..048245237855212afe1f3ec4795b2253026ef864 100644
|
||||
--- a/ipaserver/install/ipa_cacert_manage.py
|
||||
+++ b/ipaserver/install/ipa_cacert_manage.py
|
||||
@@ -22,14 +22,13 @@ from __future__ import print_function, absolute_import
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
-from optparse import OptionGroup # pylint: disable=deprecated-module
|
||||
import gssapi
|
||||
|
||||
from ipalib.constants import (
|
||||
RENEWAL_CA_NAME, RENEWAL_REUSE_CA_NAME, RENEWAL_SELFSIGNED_CA_NAME,
|
||||
IPA_CA_CN)
|
||||
from ipalib.install import certmonger, certstore
|
||||
-from ipapython import admintool, ipautil
|
||||
+from ipapython import admintool, ipautil, config
|
||||
from ipapython.certdb import (EMPTY_TRUST_FLAGS,
|
||||
EXTERNAL_CA_TRUST_FLAGS,
|
||||
TrustFlags,
|
||||
@@ -61,7 +60,7 @@ class CACertManage(admintool.AdminTool):
|
||||
"-p", "--password", dest='password',
|
||||
help="Directory Manager password")
|
||||
|
||||
- renew_group = OptionGroup(parser, "Renew options")
|
||||
+ renew_group = config.OptionGroup(parser, "Renew options")
|
||||
renew_group.add_option(
|
||||
"--self-signed", dest='self_signed',
|
||||
action='store_true',
|
||||
@@ -89,7 +88,7 @@ class CACertManage(admintool.AdminTool):
|
||||
"certificate chain")
|
||||
parser.add_option_group(renew_group)
|
||||
|
||||
- install_group = OptionGroup(parser, "Install options")
|
||||
+ install_group = config.OptionGroup(parser, "Install options")
|
||||
install_group.add_option(
|
||||
"-n", "--nickname", dest='nickname',
|
||||
help="Nickname for the certificate")
|
||||
@@ -98,7 +97,7 @@ class CACertManage(admintool.AdminTool):
|
||||
help="Trust flags for the certificate in certutil format")
|
||||
parser.add_option_group(install_group)
|
||||
|
||||
- delete_group = OptionGroup(parser, "Delete options")
|
||||
+ delete_group = config.OptionGroup(parser, "Delete options")
|
||||
delete_group.add_option(
|
||||
"-f", "--force", action='store_true',
|
||||
help="Force removing the CA even if chain validation fails")
|
||||
diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
|
||||
index 3e4cd67fa677db2534a639eb6beb14dfd78bf035..8a09179f7fa0c2ddad72d01e9c3eaf98575d0a88 100644
|
||||
--- a/ipaserver/install/ipa_kra_install.py
|
||||
+++ b/ipaserver/install/ipa_kra_install.py
|
||||
@@ -22,13 +22,12 @@ from __future__ import print_function, absolute_import
|
||||
import logging
|
||||
import sys
|
||||
import tempfile
|
||||
-from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module
|
||||
|
||||
from textwrap import dedent
|
||||
from ipalib import api
|
||||
from ipalib.constants import DOMAIN_LEVEL_1
|
||||
from ipaplatform.paths import paths
|
||||
-from ipapython import admintool
|
||||
+from ipapython import admintool, config
|
||||
from ipaserver.install import service
|
||||
from ipaserver.install import cainstance
|
||||
from ipaserver.install import custodiainstance
|
||||
@@ -73,7 +72,7 @@ class KRAInstall(admintool.AdminTool):
|
||||
parser.add_option(
|
||||
"--uninstall",
|
||||
dest="uninstall", action="store_true", default=False,
|
||||
- help=SUPPRESS_HELP)
|
||||
+ help=config.SUPPRESS_HELP)
|
||||
|
||||
parser.add_option(
|
||||
"--pki-config-override", dest="pki_config_override",
|
||||
diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
|
||||
index 57ad8dd05206132d3458fa84e7fb996b135f7f71..8d75a0e6beba09086bc2ce8c73f3bcfe81e52113 100644
|
||||
--- a/ipaserver/install/ipa_restore.py
|
||||
+++ b/ipaserver/install/ipa_restore.py
|
||||
@@ -20,7 +20,6 @@
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import logging
|
||||
-import optparse # pylint: disable=deprecated-module
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
@@ -34,7 +33,7 @@ import six
|
||||
from ipaclient.install.client import update_ipa_nssdb
|
||||
from ipalib import api, errors
|
||||
from ipalib.constants import FQDN
|
||||
-from ipapython import version, ipautil
|
||||
+from ipapython import version, ipautil, config
|
||||
from ipapython.ipautil import run, user_input
|
||||
from ipapython import admintool, certdb
|
||||
from ipapython.dn import DN
|
||||
@@ -190,7 +189,7 @@ class Restore(admintool.AdminTool):
|
||||
help="Directory Manager password")
|
||||
parser.add_option(
|
||||
"--gpg-keyring", dest="gpg_keyring",
|
||||
- help=optparse.SUPPRESS_HELP)
|
||||
+ help=config.SUPPRESS_HELP)
|
||||
parser.add_option(
|
||||
"--data", dest="data_only", action="store_true",
|
||||
default=False, help="Restore only the data")
|
||||
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
|
||||
index e29f00ec37779aeb10255c5df6e10d6ecc0a6d11..e9f680b1d1e505f89fc1611b8dbb3f84768f6781 100644
|
||||
--- a/ipaserver/install/ipa_server_certinstall.py
|
||||
+++ b/ipaserver/install/ipa_server_certinstall.py
|
||||
@@ -22,12 +22,11 @@ from __future__ import print_function, absolute_import
|
||||
import os
|
||||
import os.path
|
||||
import tempfile
|
||||
-import optparse # pylint: disable=deprecated-module
|
||||
|
||||
from ipalib import x509
|
||||
from ipalib.install import certmonger
|
||||
from ipaplatform.paths import paths
|
||||
-from ipapython import admintool, dogtag
|
||||
+from ipapython import admintool, dogtag, config
|
||||
from ipapython.certdb import NSSDatabase, get_ca_nickname
|
||||
from ipapython.dn import DN
|
||||
from ipapython import ipaldap
|
||||
@@ -65,8 +64,8 @@ class ServerCertInstall(admintool.AdminTool):
|
||||
help="The password of the PKCS#12 file")
|
||||
parser.add_option(
|
||||
"--dirsrv_pin", "--http_pin",
|
||||
- dest="pin",
|
||||
- help=optparse.SUPPRESS_HELP)
|
||||
+ dest="pin", sensitive=True,
|
||||
+ help=config.SUPPRESS_HELP)
|
||||
parser.add_option(
|
||||
"--cert-name",
|
||||
dest="cert_name", metavar="NAME",
|
||||
diff --git a/ipatests/i18n.py b/ipatests/i18n.py
|
||||
index 49f5c4c3232346db3147bd7a5ba8056344ac907f..57915c286be72124fa23380f97f3922496f00c22 100644
|
||||
--- a/ipatests/i18n.py
|
||||
+++ b/ipatests/i18n.py
|
||||
@@ -22,7 +22,6 @@ from __future__ import print_function
|
||||
|
||||
# WARNING: Do not import ipa modules, this is also used as a
|
||||
# stand-alone script (invoked from po Makefile).
|
||||
-import optparse # pylint: disable=deprecated-module
|
||||
import sys
|
||||
import gettext
|
||||
import re
|
||||
@@ -30,6 +29,7 @@ import os
|
||||
import traceback
|
||||
import polib
|
||||
from collections import namedtuple
|
||||
+from ipapython import config
|
||||
|
||||
import six
|
||||
|
||||
@@ -722,9 +722,9 @@ usage ='''
|
||||
def main():
|
||||
global verbose, print_traceback, pedantic, show_strings
|
||||
|
||||
- parser = optparse.OptionParser(usage=usage)
|
||||
+ parser = config.IPAOptionParser(usage=usage)
|
||||
|
||||
- mode_group = optparse.OptionGroup(parser, 'Operational Mode',
|
||||
+ mode_group = config.OptionGroup(parser, 'Operational Mode',
|
||||
'You must select one these modes to run in')
|
||||
|
||||
mode_group.add_option('-g', '--test-gettext', action='store_const', const='test_gettext', dest='mode',
|
||||
@@ -748,7 +748,7 @@ def main():
|
||||
parser.add_option('--traceback', action='store_true', dest='print_traceback', default=False,
|
||||
help='print the traceback when an exception occurs')
|
||||
|
||||
- param_group = optparse.OptionGroup(parser, 'Run Time Parameters',
|
||||
+ param_group = config.OptionGroup(parser, 'Run Time Parameters',
|
||||
'These may be used to modify the run time defaults')
|
||||
|
||||
param_group.add_option('--test-lang', action='store', dest='test_lang', default='test',
|
||||
diff --git a/makeapi.in b/makeapi.in
|
||||
index a801b9253db9500e0510fe591074ccf2e6d752c1..8fc87d23de743a6d661112ee616dce129a785a36 100644
|
||||
--- a/makeapi.in
|
||||
+++ b/makeapi.in
|
||||
@@ -38,6 +38,7 @@ from ipalib.parameters import Param
|
||||
from ipalib.output import Output
|
||||
from ipalib.text import Gettext, NGettext, ConcatenatedLazyText
|
||||
from ipalib.capabilities import capabilities
|
||||
+from ipapython import config
|
||||
|
||||
API_FILE='API.txt'
|
||||
|
||||
@@ -84,9 +85,7 @@ OUTPUT_IGNORED_ATTRIBUTES = (
|
||||
)
|
||||
|
||||
def parse_options():
|
||||
- from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
-
|
||||
- parser = OptionParser()
|
||||
+ parser = config.IPAOptionParser()
|
||||
parser.add_option("--validate", dest="validate", action="store_true",
|
||||
default=False, help="Validate the API vs the stored API")
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,422 +0,0 @@
|
||||
From f9314562aaae03619eb89ae762bc24182174ad28 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Fri, 8 Nov 2024 14:59:20 +0200
|
||||
Subject: [PATCH] ipa tools: remove sensitive material from the commandline
|
||||
|
||||
When command line tools accept passwords, remove them from the command
|
||||
line so that they don't get visible in '/proc/pid/commandline'.
|
||||
|
||||
There is no common method to access the original ARGV vector and modify
|
||||
it from Python. Since this mostly affects Linux systems where IPA
|
||||
services run, we expect use of GNU libc and thus can rely on internal
|
||||
glibc symbols. If they aren't available, the code will skip removing
|
||||
passwords.
|
||||
|
||||
Fixes: CVE-2024-11029
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
.../com.redhat.idm.trust-fetch-domains.in | 5 ++-
|
||||
install/tools/ipa-adtrust-install.in | 3 +-
|
||||
install/tools/ipa-ca-install.in | 2 +
|
||||
install/tools/ipa-compat-manage.in | 6 ++-
|
||||
install/tools/ipa-csreplica-manage.in | 12 +++---
|
||||
install/tools/ipa-managed-entries.in | 5 ++-
|
||||
install/tools/ipa-replica-conncheck.in | 7 ++--
|
||||
install/tools/ipa-replica-manage.in | 10 +++--
|
||||
ipapython/admintool.py | 40 +++++++++++++++++++
|
||||
ipaserver/install/ipa_migrate.py | 17 +++++++-
|
||||
ipaserver/install/ipa_restore.py | 2 +-
|
||||
ipaserver/install/ipa_server_certinstall.py | 2 +-
|
||||
12 files changed, 90 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains.in b/install/oddjob/com.redhat.idm.trust-fetch-domains.in
|
||||
index 45c1f1463d5849d753c2913aa0b86b845cfce784..b86be0212c462e82d9379f55d5d3198c1017d2e7 100644
|
||||
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains.in
|
||||
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains.in
|
||||
@@ -15,6 +15,7 @@ import six
|
||||
import gssapi
|
||||
|
||||
from ipalib.install.kinit import kinit_keytab, kinit_password
|
||||
+from ipapython.admintool import admin_cleanup_global_argv
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
@@ -52,11 +53,13 @@ def parse_options():
|
||||
"--password",
|
||||
action="store",
|
||||
dest="password",
|
||||
- help="Display debugging information",
|
||||
+ help="Password for Active Directory administrator",
|
||||
+ sensitive=True
|
||||
)
|
||||
|
||||
options, args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
+ admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
# We only use first argument of the passed args but as D-BUS interface
|
||||
# in oddjobd cannot expose optional, we fill in empty slots from IPA side
|
||||
diff --git a/install/tools/ipa-adtrust-install.in b/install/tools/ipa-adtrust-install.in
|
||||
index e7b0e369259da5d28d703558d9293ccfaf68f3ed..1efccdb678d17410667b1721670bf6bf79152109 100644
|
||||
--- a/install/tools/ipa-adtrust-install.in
|
||||
+++ b/install/tools/ipa-adtrust-install.in
|
||||
@@ -35,7 +35,7 @@ from ipaserver.install.installutils import (
|
||||
read_password,
|
||||
check_server_configuration,
|
||||
run_script)
|
||||
-from ipapython.admintool import ScriptError
|
||||
+from ipapython.admintool import ScriptError, admin_cleanup_global_argv
|
||||
from ipapython import version
|
||||
from ipapython import ipautil
|
||||
from ipalib import api, errors, krb_utils
|
||||
@@ -93,6 +93,7 @@ def parse_options():
|
||||
|
||||
options, _args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
+ admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
return safe_options, options
|
||||
|
||||
diff --git a/install/tools/ipa-ca-install.in b/install/tools/ipa-ca-install.in
|
||||
index 9f3d16669679a245b73e044622ff52321524fcde..b437e761f760ab715c27bc330ac0f2e66e72ef5b 100644
|
||||
--- a/install/tools/ipa-ca-install.in
|
||||
+++ b/install/tools/ipa-ca-install.in
|
||||
@@ -42,6 +42,7 @@ from ipalib.constants import DOMAIN_LEVEL_1
|
||||
from ipapython.config import IPAOptionParser
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipaplatform.paths import paths
|
||||
+from ipapython.admintool import admin_cleanup_global_argv
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
|
||||
@@ -132,6 +133,7 @@ def parse_options():
|
||||
|
||||
options, args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
+ admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
if args:
|
||||
parser.error("Too many arguments provided")
|
||||
diff --git a/install/tools/ipa-compat-manage.in b/install/tools/ipa-compat-manage.in
|
||||
index 459f39fc826bbe6becd8be3517235af343d4b0d9..9650abd6f83ebc0a8ef347fee83989d4e9f13f09 100644
|
||||
--- a/install/tools/ipa-compat-manage.in
|
||||
+++ b/install/tools/ipa-compat-manage.in
|
||||
@@ -24,13 +24,13 @@ from __future__ import print_function
|
||||
import sys
|
||||
from ipaplatform.paths import paths
|
||||
try:
|
||||
- from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
from ipapython import ipautil, config
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
from ipalib import api, errors
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipapython.dn import DN
|
||||
+ from ipapython.admintool import admin_cleanup_global_argv
|
||||
except ImportError as e:
|
||||
print("""\
|
||||
There was a problem importing one of the required Python modules. The
|
||||
@@ -46,7 +46,8 @@ nis_config_dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
def parse_options():
|
||||
usage = "%prog [options] <enable|disable|status>\n"
|
||||
usage += "%prog [options]\n"
|
||||
- parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
|
||||
+ parser = config.IPAOptionParser(usage=usage,
|
||||
+ formatter=config.IPAFormatter())
|
||||
|
||||
parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
help="Display debugging information about the update(s)")
|
||||
@@ -55,6 +56,7 @@ def parse_options():
|
||||
|
||||
config.add_standard_options(parser)
|
||||
options, args = parser.parse_args()
|
||||
+ admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
return options, args
|
||||
|
||||
diff --git a/install/tools/ipa-csreplica-manage.in b/install/tools/ipa-csreplica-manage.in
|
||||
index 6f248cc50f7f81b2014d629355f6f32d1d6ab7be..2fab27a94cfdbef8faadca82bc222bf96d212159 100644
|
||||
--- a/install/tools/ipa-csreplica-manage.in
|
||||
+++ b/install/tools/ipa-csreplica-manage.in
|
||||
@@ -32,8 +32,8 @@ from ipaserver.install import (replication, installutils, bindinstance,
|
||||
from ipalib import api, errors
|
||||
from ipalib.constants import FQDN
|
||||
from ipalib.util import has_managed_topology, print_replication_status
|
||||
-from ipapython import ipautil, ipaldap, version
|
||||
-from ipapython.admintool import ScriptError
|
||||
+from ipapython import ipautil, ipaldap, version, config
|
||||
+from ipapython.admintool import admin_cleanup_global_argv, ScriptError
|
||||
from ipapython.dn import DN
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
@@ -54,11 +54,10 @@ commands = {
|
||||
|
||||
|
||||
def parse_options():
|
||||
- from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
-
|
||||
- parser = OptionParser(version=version.VERSION)
|
||||
+ parser = config.IPAOptionParser(version=version.VERSION)
|
||||
parser.add_option("-H", "--host", dest="host", help="starting host")
|
||||
- parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
|
||||
+ parser.add_option("-p", "--password", dest="dirman_passwd", sensitive=True,
|
||||
+ help="Directory Manager password")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
|
||||
help="provide additional information")
|
||||
parser.add_option("-f", "--force", dest="force", action="store_true", default=False,
|
||||
@@ -66,6 +65,7 @@ def parse_options():
|
||||
parser.add_option("--from", dest="fromhost", help="Host to get data from")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
+ admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
valid_syntax = False
|
||||
|
||||
diff --git a/install/tools/ipa-managed-entries.in b/install/tools/ipa-managed-entries.in
|
||||
index e3f121943eb3b18ca8f7f8bfeae7813cbc9bd753..ff2fd6a58ccb5b322f75008578fea81f561666fa 100644
|
||||
--- a/install/tools/ipa-managed-entries.in
|
||||
+++ b/install/tools/ipa-managed-entries.in
|
||||
@@ -24,7 +24,6 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
-from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython import config
|
||||
@@ -32,6 +31,7 @@ from ipaserver.install import installutils
|
||||
from ipalib import api, errors
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipapython.dn import DN
|
||||
+from ipapython.admintool import admin_cleanup_global_argv
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
|
||||
@@ -51,9 +51,10 @@ def parse_options():
|
||||
action="store_true",
|
||||
help="List available Managed Entries")
|
||||
parser.add_option("-p", "--password", dest="dirman_password",
|
||||
- help="Directory Manager password")
|
||||
+ sensitive=True, help="Directory Manager password")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
+ admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
return options, args
|
||||
|
||||
diff --git a/install/tools/ipa-replica-conncheck.in b/install/tools/ipa-replica-conncheck.in
|
||||
index 8eee82483abc275cb37ad96ff272b6ee8192403f..81b7d13ac900031fa81356b894dc3eaaaee0f744 100644
|
||||
--- a/install/tools/ipa-replica-conncheck.in
|
||||
+++ b/install/tools/ipa-replica-conncheck.in
|
||||
@@ -23,15 +23,15 @@ from __future__ import print_function
|
||||
import logging
|
||||
|
||||
from ipapython import ipachangeconf
|
||||
-from ipapython.config import IPAOptionParser
|
||||
+from ipapython.config import (IPAOptionParser, OptionGroup,
|
||||
+ OptionValueError)
|
||||
+from ipapython.admintool import admin_cleanup_global_argv
|
||||
from ipapython.dn import DN
|
||||
from ipapython import version
|
||||
from ipapython import ipautil, certdb
|
||||
from ipalib import api, errors, x509
|
||||
from ipalib.constants import FQDN
|
||||
from ipaserver.install import installutils
|
||||
-# pylint: disable=deprecated-module
|
||||
-from optparse import OptionGroup, OptionValueError
|
||||
# pylint: enable=deprecated-module
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
import copy
|
||||
@@ -189,6 +189,7 @@ def parse_options():
|
||||
|
||||
options, _args = parser.parse_args()
|
||||
safe_options = parser.get_safe_opts(options)
|
||||
+ admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
if options.master and options.replica:
|
||||
parser.error("on-master and on-replica options are mutually exclusive!")
|
||||
diff --git a/install/tools/ipa-replica-manage.in b/install/tools/ipa-replica-manage.in
|
||||
index d6e6ef57c39af70f164d41662227af3dc2535f9c..7e5b31a598537f57c471729f22fdec4a5bedc03d 100644
|
||||
--- a/install/tools/ipa-replica-manage.in
|
||||
+++ b/install/tools/ipa-replica-manage.in
|
||||
@@ -43,6 +43,7 @@ from ipalib.util import (
|
||||
print_replication_status,
|
||||
verify_host_resolvable,
|
||||
)
|
||||
+from ipapython.admintool import admin_cleanup_global_argv
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
from ipapython.dn import DN
|
||||
from ipapython.config import IPAOptionParser
|
||||
@@ -84,7 +85,8 @@ class NoRUVsFound(Exception):
|
||||
def parse_options():
|
||||
parser = IPAOptionParser(version=version.VERSION)
|
||||
parser.add_option("-H", "--host", dest="host", help="starting host")
|
||||
- parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
|
||||
+ parser.add_option("-p", "--password", dest="dirman_passwd", sensitive=True,
|
||||
+ help="Directory Manager password")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
|
||||
help="provide additional information")
|
||||
parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False,
|
||||
@@ -95,7 +97,7 @@ def parse_options():
|
||||
help="DANGER: clean up references to a ghost master")
|
||||
parser.add_option("--binddn", dest="binddn", default=None, type="dn",
|
||||
help="Bind DN to use with remote server")
|
||||
- parser.add_option("--bindpw", dest="bindpw", default=None,
|
||||
+ parser.add_option("--bindpw", dest="bindpw", default=None, sensitive=True,
|
||||
help="Password for Bind DN to use with remote server")
|
||||
parser.add_option("--winsync", dest="winsync", action="store_true", default=False,
|
||||
help="This is a Windows Sync Agreement")
|
||||
@@ -103,13 +105,15 @@ def parse_options():
|
||||
help="Full path and filename of CA certificate to use with TLS/SSL to the remote server")
|
||||
parser.add_option("--win-subtree", dest="win_subtree", default=None,
|
||||
help="DN of Windows subtree containing the users you want to sync (default cn=Users,<domain suffix)")
|
||||
- parser.add_option("--passsync", dest="passsync", default=None,
|
||||
+ parser.add_option("--passsync", dest="passsync",
|
||||
+ default=None, sensitive=True,
|
||||
help="Password for the IPA system user used by the Windows PassSync plugin to synchronize passwords")
|
||||
parser.add_option("--from", dest="fromhost", help="Host to get data from")
|
||||
parser.add_option("--no-lookup", dest="nolookup", action="store_true", default=False,
|
||||
help="do not perform DNS lookup checks")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
+ admin_cleanup_global_argv(parser, options, sys.argv)
|
||||
|
||||
valid_syntax = False
|
||||
|
||||
diff --git a/ipapython/admintool.py b/ipapython/admintool.py
|
||||
index dff9112eba4009d2fc1a6202756c6671ba4d909d..602223ef627c99f36a27d28fa712a6c196d7cb9f 100644
|
||||
--- a/ipapython/admintool.py
|
||||
+++ b/ipapython/admintool.py
|
||||
@@ -39,6 +39,45 @@ SERVER_NOT_CONFIGURED = 2
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
+def admin_cleanup_global_argv(option_parser, options, argv):
|
||||
+ """Takes option parser and generated options and scrubs sensitive arguments
|
||||
+ from the global program arguments. Note that this only works for GNU GLIBC
|
||||
+ as Python has no generic way to get access to the original argv values to
|
||||
+ modify them in place.
|
||||
+
|
||||
+ The code assumes Python behavior, e.g. there are two additional args in the
|
||||
+ list (/path/to/python -I ...) than what's passed as 'argv' here.
|
||||
+ """
|
||||
+ import ctypes
|
||||
+ import ctypes.util
|
||||
+ try:
|
||||
+ _c = ctypes.CDLL(ctypes.util.find_library("c"))
|
||||
+ if _c._name is None:
|
||||
+ return
|
||||
+ _argv = ctypes.POINTER(ctypes.c_voidp).in_dll(_c, "_dl_argv")
|
||||
+ # since we run as 'python -I <executable> ...', add two args
|
||||
+ _argc = len(argv) + 2
|
||||
+ all_options = []
|
||||
+ if '_get_all_options' in dir(option_parser):
|
||||
+ # OptParse parser
|
||||
+ all_options = option_parser._get_all_options()
|
||||
+ elif '_actions' in dir(option_parser):
|
||||
+ # ArgParse parser
|
||||
+ all_options = option_parser._actions
|
||||
+
|
||||
+ for opt in all_options:
|
||||
+ if getattr(opt, 'sensitive', False):
|
||||
+ v = getattr(options, opt.dest)
|
||||
+ for i in range(0, _argc):
|
||||
+ vi = ctypes.cast(_argv[i],
|
||||
+ ctypes.c_char_p
|
||||
+ ).value.decode('utf-8')
|
||||
+ if vi == v:
|
||||
+ ctypes.memset(_argv[i], ord('X'), len(v))
|
||||
+ except Exception:
|
||||
+ pass
|
||||
+
|
||||
+
|
||||
class ScriptError(Exception):
|
||||
"""An exception that records an error message and a return value
|
||||
"""
|
||||
@@ -148,6 +187,7 @@ class AdminTool:
|
||||
cls._option_parsers[cls] = cls.option_parser
|
||||
|
||||
options, args = cls.option_parser.parse_args(argv[1:])
|
||||
+ admin_cleanup_global_argv(cls.option_parser, options, argv)
|
||||
|
||||
command_class = cls.get_command_class(options, args)
|
||||
command = command_class(options, args)
|
||||
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
|
||||
index f35629378490d3d45ca97f2aa5b4390c67d660ed..ece473bc8cb525e2d563356b5b274502d6b703e8 100644
|
||||
--- a/ipaserver/install/ipa_migrate.py
|
||||
+++ b/ipaserver/install/ipa_migrate.py
|
||||
@@ -28,6 +28,7 @@ from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipaldap import LDAPClient, LDAPEntry, realm_to_ldapi_uri
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
+from ipapython.admintool import admin_cleanup_global_argv
|
||||
from ipaserver.install.ipa_migrate_constants import (
|
||||
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
|
||||
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
|
||||
@@ -284,6 +285,18 @@ class LDIFParser(ldif.LDIFParser):
|
||||
self.mc.process_db_entry(entry_dn=dn, entry_attrs=entry_attrs)
|
||||
|
||||
|
||||
+class SensitiveStoreAction(argparse._StoreAction):
|
||||
+ def __init__(self, *, sensitive, **options):
|
||||
+ super(SensitiveStoreAction, self).__init__(**options)
|
||||
+ self.sensitive = sensitive
|
||||
+
|
||||
+ def _get_kwargs(self):
|
||||
+ names = super(SensitiveStoreAction, self)._get_kwargs()
|
||||
+ sensitive_name = 'sensitive'
|
||||
+ names.extend((sensitive_name, getattr(self, sensitive_name)))
|
||||
+ return names
|
||||
+
|
||||
+
|
||||
#
|
||||
# Migrate IPA to IPA Class
|
||||
#
|
||||
@@ -344,7 +357,8 @@ class IPAMigrate():
|
||||
help='Password for the Bind DN. If a password '
|
||||
'is not provided then the user will be '
|
||||
'prompted to enter it',
|
||||
- default=None)
|
||||
+ default=None, sensitive=True,
|
||||
+ action=SensitiveStoreAction)
|
||||
parser.add_argument('-j', '--bind-pw-file',
|
||||
help='A text file containing the clear text '
|
||||
'password for the Bind DN', default=None)
|
||||
@@ -2128,6 +2142,7 @@ class IPAMigrate():
|
||||
parser = argparse.ArgumentParser(description=desc, allow_abbrev=True)
|
||||
self.add_options(parser)
|
||||
self.validate_options()
|
||||
+ admin_cleanup_global_argv(parser, self.args, sys.argv)
|
||||
|
||||
# Check for dryrun mode
|
||||
if self.args.dryrun or self.args.dryrun_record is not None:
|
||||
diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
|
||||
index 8d75a0e6beba09086bc2ce8c73f3bcfe81e52113..539501ab4f0c73571b680aeccf3854b511fd29dc 100644
|
||||
--- a/ipaserver/install/ipa_restore.py
|
||||
+++ b/ipaserver/install/ipa_restore.py
|
||||
@@ -185,7 +185,7 @@ class Restore(admintool.AdminTool):
|
||||
super(Restore, cls).add_options(parser, debug_option=True)
|
||||
|
||||
parser.add_option(
|
||||
- "-p", "--password", dest="password",
|
||||
+ "-p", "--password", dest="password", sensitive=True,
|
||||
help="Directory Manager password")
|
||||
parser.add_option(
|
||||
"--gpg-keyring", dest="gpg_keyring",
|
||||
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
|
||||
index e9f680b1d1e505f89fc1611b8dbb3f84768f6781..76ad37ca7bcc62364379d56b21ead43c5248f5f1 100644
|
||||
--- a/ipaserver/install/ipa_server_certinstall.py
|
||||
+++ b/ipaserver/install/ipa_server_certinstall.py
|
||||
@@ -72,7 +72,7 @@ class ServerCertInstall(admintool.AdminTool):
|
||||
help="Name of the certificate to install")
|
||||
parser.add_option(
|
||||
"-p", "--dirman-password",
|
||||
- dest="dirman_password",
|
||||
+ dest="dirman_password", sensitive=True,
|
||||
help="Directory Manager password")
|
||||
|
||||
def validate_options(self):
|
||||
--
|
||||
2.47.1
|
||||
|
||||
49
SOURCES/0044-ipatests-fix-migration-test.patch
Normal file
49
SOURCES/0044-ipatests-fix-migration-test.patch
Normal file
@ -0,0 +1,49 @@
|
||||
From 3d5fe37d41332cdca486c9ca42089934e5ed7f89 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Mon, 30 Mar 2026 10:39:51 +0200
|
||||
Subject: [PATCH] ipatests: fix migration test
|
||||
|
||||
A previous commit modified the DNS zone forwarder's IP address
|
||||
but forgot to change the expected value in the test.
|
||||
Use a constant in the test setup and in the test expectation.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9964
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Sudhir Menon <sumenon@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_ipa_ipa_migration.py | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
index 105c79b..436e222 100644
|
||||
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
|
||||
@@ -15,6 +15,7 @@ import pytest
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
+TEST_ZONE_FORWARDER = "10.11.12.13"
|
||||
|
||||
def prepare_ipa_server(master):
|
||||
"""
|
||||
@@ -227,7 +228,7 @@ def prepare_ipa_server(master):
|
||||
"dnsforwardzone-add",
|
||||
"forwardzone.test",
|
||||
"--forwarder",
|
||||
- "192.168.124.10",
|
||||
+ TEST_ZONE_FORWARDER,
|
||||
]
|
||||
)
|
||||
|
||||
@@ -827,7 +828,7 @@ class TestIPAMigrateCLIOptions(MigrationTest):
|
||||
)
|
||||
assert 'Zone name: {}'.format(zone_name) in result.stdout_text
|
||||
assert 'Active zone: True' in result.stdout_text
|
||||
- assert 'Zone forwarders: 10.11.12.13' in result.stdout_text
|
||||
+ assert f'Zone forwarders: {TEST_ZONE_FORWARDER}' in result.stdout_text
|
||||
assert 'Forward policy: first' in result.stdout_text
|
||||
|
||||
def test_ipa_migrate_version_option(self):
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
From d857fcfcc21481cdf06b8cce1685e141921d2fbf Mon Sep 17 00:00:00 2001
|
||||
From: Sumit Bose <sbose@redhat.com>
|
||||
Date: Wed, 27 Nov 2024 12:16:09 +0100
|
||||
Subject: [PATCH] ipa-otpd: use oidc_child's --client-secret-stdin option
|
||||
|
||||
To remove the client secret from the command line where it would be
|
||||
visible e.g. when calling ps it is now passed via stdin to oidc_child.
|
||||
|
||||
Fixes: CVE-2024-11029
|
||||
|
||||
Signed-off-by: Sumit Bose <sbose@redhat.com>
|
||||
---
|
||||
daemons/ipa-otpd/oauth2.c | 24 ++++++++++++++----------
|
||||
1 file changed, 14 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-otpd/oauth2.c b/daemons/ipa-otpd/oauth2.c
|
||||
index a33cf51715ca2a3e7a0cef871aed5cfbbd037598..52d7d7c9cb6c410bdbaa2e5eddccfea2204d3e69 100644
|
||||
--- a/daemons/ipa-otpd/oauth2.c
|
||||
+++ b/daemons/ipa-otpd/oauth2.c
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/random.h>
|
||||
+#include <sys/uio.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -93,6 +94,7 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
|
||||
(void)vctx; /* Unused */
|
||||
ssize_t io;
|
||||
struct child_ctx *child_ctx;
|
||||
+ struct iovec iov[3];
|
||||
|
||||
child_ctx = verto_get_private(ev);
|
||||
if (child_ctx == NULL) {
|
||||
@@ -102,15 +104,18 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
|
||||
}
|
||||
|
||||
if (child_ctx->oauth2_state == OAUTH2_GET_DEVICE_CODE) {
|
||||
- /* no input needed */
|
||||
- verto_del(ev);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
+ io = write(verto_get_fd(ev), child_ctx->item->idp.ipaidpClientSecret,
|
||||
+ strlen(child_ctx->item->idp.ipaidpClientSecret));
|
||||
+ } else {
|
||||
+ iov[0].iov_base = child_ctx->item->idp.ipaidpClientSecret;
|
||||
+ iov[0].iov_len = strlen(child_ctx->item->idp.ipaidpClientSecret);
|
||||
+ iov[1].iov_base = "\n";
|
||||
+ iov[1].iov_len = 1;
|
||||
+ iov[2].iov_base = child_ctx->saved_item->oauth2.device_code_reply;
|
||||
+ iov[2].iov_len = strlen(child_ctx->saved_item->oauth2.device_code_reply);
|
||||
|
||||
- io = write(verto_get_fd(ev),
|
||||
- child_ctx->saved_item->oauth2.device_code_reply,
|
||||
- strlen(child_ctx->saved_item->oauth2.device_code_reply));
|
||||
+ io = writev(verto_get_fd(ev), iov, 3);
|
||||
+ }
|
||||
otpd_queue_item_free(child_ctx->saved_item);
|
||||
|
||||
if (io < 0) {
|
||||
@@ -429,8 +434,7 @@ int oauth2(struct otpd_queue_item **item, enum oauth2_state oauth2_state)
|
||||
args[args_idx++] = (*item)->idp.ipaidpClientID;
|
||||
|
||||
if ((*item)->idp.ipaidpClientSecret) {
|
||||
- args[args_idx++] = "--client-secret";
|
||||
- args[args_idx++] = (*item)->idp.ipaidpClientSecret;
|
||||
+ args[args_idx++] = "--client-secret-stdin";
|
||||
}
|
||||
|
||||
if ((*item)->idp.ipaidpScope) {
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,69 +0,0 @@
|
||||
From eef544c1d331bbe80852ebe8b5fc9bad0539b6fa Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Wed, 15 Jan 2025 15:39:20 +0100
|
||||
Subject: [PATCH] Fix pylint issue in ipatests/i18n.py
|
||||
|
||||
This file should not import ipa modules
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
---
|
||||
ipatests/i18n.py | 8 ++++----
|
||||
pylintrc | 3 ++-
|
||||
2 files changed, 6 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/ipatests/i18n.py b/ipatests/i18n.py
|
||||
index 57915c286be72124fa23380f97f3922496f00c22..49f5c4c3232346db3147bd7a5ba8056344ac907f 100644
|
||||
--- a/ipatests/i18n.py
|
||||
+++ b/ipatests/i18n.py
|
||||
@@ -22,6 +22,7 @@ from __future__ import print_function
|
||||
|
||||
# WARNING: Do not import ipa modules, this is also used as a
|
||||
# stand-alone script (invoked from po Makefile).
|
||||
+import optparse # pylint: disable=deprecated-module
|
||||
import sys
|
||||
import gettext
|
||||
import re
|
||||
@@ -29,7 +30,6 @@ import os
|
||||
import traceback
|
||||
import polib
|
||||
from collections import namedtuple
|
||||
-from ipapython import config
|
||||
|
||||
import six
|
||||
|
||||
@@ -722,9 +722,9 @@ usage ='''
|
||||
def main():
|
||||
global verbose, print_traceback, pedantic, show_strings
|
||||
|
||||
- parser = config.IPAOptionParser(usage=usage)
|
||||
+ parser = optparse.OptionParser(usage=usage)
|
||||
|
||||
- mode_group = config.OptionGroup(parser, 'Operational Mode',
|
||||
+ mode_group = optparse.OptionGroup(parser, 'Operational Mode',
|
||||
'You must select one these modes to run in')
|
||||
|
||||
mode_group.add_option('-g', '--test-gettext', action='store_const', const='test_gettext', dest='mode',
|
||||
@@ -748,7 +748,7 @@ def main():
|
||||
parser.add_option('--traceback', action='store_true', dest='print_traceback', default=False,
|
||||
help='print the traceback when an exception occurs')
|
||||
|
||||
- param_group = config.OptionGroup(parser, 'Run Time Parameters',
|
||||
+ param_group = optparse.OptionGroup(parser, 'Run Time Parameters',
|
||||
'These may be used to modify the run time defaults')
|
||||
|
||||
param_group.add_option('--test-lang', action='store', dest='test_lang', default='test',
|
||||
diff --git a/pylintrc b/pylintrc
|
||||
index 50278cc76031af68959a138f6ea185c4288c7155..8fadeffbdfdb8013135a17b3493ecc4dc7e5e001 100644
|
||||
--- a/pylintrc
|
||||
+++ b/pylintrc
|
||||
@@ -153,4 +153,5 @@ forbidden-imports=
|
||||
ipaplatform/:ipaclient:ipalib:ipaserver,
|
||||
ipapython/:ipaclient:ipalib:ipaserver
|
||||
ipatests/pytest_ipa:ipaserver:ipaclient.install:ipalib.install
|
||||
- ipatests/test_integration:ipaserver
|
||||
+ ipatests/test_integration:ipaserver,
|
||||
+ ipatests/i18n.py:ipapython
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
From 45f96a0f978dfda0e2faa8360182a1dfd3122b94 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Fri, 10 Jan 2025 13:22:29 +0100
|
||||
Subject: [PATCH] ipatests: skip test_ipahealthcheck_ds_configcheck for recent
|
||||
versions
|
||||
|
||||
389-ds removed the parameter nsslapd-logging-hr-timestamps-enabled
|
||||
in 2.5.3 and above. Skip the test that exercises the corresponding
|
||||
healthcheck.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9730
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_ipahealthcheck.py | 12 +++++++++---
|
||||
1 file changed, 9 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
index cc51a5a6a62fbc50927fc2fc51f129a069e70b69..6b6f15aa433a423fe599118d2226e4c4ec62b13b 100644
|
||||
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
||||
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
@@ -18,7 +18,7 @@ import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
-from ipalib import x509
|
||||
+from ipalib import errors, x509
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipaldap import realm_to_serverid
|
||||
from ipapython.certdb import NSS_SQL_FILES
|
||||
@@ -1146,8 +1146,14 @@ class TestIpaHealthCheck(IntegrationTest):
|
||||
)
|
||||
entry = ldap.get_entry(dn)
|
||||
entry.single_value["nsslapd-logging-hr-timestamps-enabled"] = 'off'
|
||||
- ldap.update_entry(entry)
|
||||
-
|
||||
+ try:
|
||||
+ ldap.update_entry(entry)
|
||||
+ except errors.DatabaseError as e:
|
||||
+ expected_msg = "Unknown attribute " \
|
||||
+ "nsslapd-logging-hr-timestamps-enabled"
|
||||
+ if expected_msg in e.message:
|
||||
+ pytest.skip(
|
||||
+ "389-ds removed nsslapd-logging-hr-timestamps-enabled")
|
||||
yield
|
||||
|
||||
entry = ldap.get_entry(dn)
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
From ec94ee72714296c86ba1227a5a945a7ed0bc7fac Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Thu, 16 Jan 2025 15:43:17 +0100
|
||||
Subject: [PATCH] ipatests: restart dirsrv after time jumps
|
||||
|
||||
The test for ipa-healthcheck is moving the date in the future.
|
||||
Restart the dirsrv instance because the LDAP server is
|
||||
sensitive to large time jumps.
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_ipahealthcheck.py | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
index 6b6f15aa433a423fe599118d2226e4c4ec62b13b..7c3f5857a477070d8a9b52c04d41f35ac580c97f 100644
|
||||
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
||||
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
@@ -1634,6 +1634,11 @@ class TestIpaHealthCheck(IntegrationTest):
|
||||
grace_date = datetime.strftime(grace_date, "%Y-%m-%d 00:00:01 Z")
|
||||
self.master.run_command(['date', '-s', grace_date])
|
||||
|
||||
+ # Restart dirsrv as it doesn't like time jumps
|
||||
+ instance = realm_to_serverid(self.master.domain.realm)
|
||||
+ cmd = ["systemctl", "restart", "dirsrv@{}".format(instance)]
|
||||
+ self.master.run_command(cmd)
|
||||
+
|
||||
for check in ("IPACertmongerExpirationCheck",
|
||||
"IPACertfileExpirationCheck",):
|
||||
execute_expiring_check(check)
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
From f12c4ed600e9b35c930d386b37e36064fbf83968 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Fri, 17 Jan 2025 09:44:22 +0200
|
||||
Subject: [PATCH] ipa-otpd: do not pass OIDC client secret if there is none to
|
||||
pass
|
||||
|
||||
If there is no client secret specified for the OIDC client, don't push
|
||||
it to oidc_child via stdin. oidc_child does only expect client secret if
|
||||
--client-secret-stdin option was specified and we already specify it
|
||||
only if client secret is not empty.
|
||||
|
||||
In addition, if client secret is empty (it is a public OIDC client),
|
||||
then strlen(NULL) would crash in glibc internals. Avoid that!
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9734
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
daemons/ipa-otpd/oauth2.c | 29 +++++++++++++++++++----------
|
||||
1 file changed, 19 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-otpd/oauth2.c b/daemons/ipa-otpd/oauth2.c
|
||||
index 52d7d7c9cb6c410bdbaa2e5eddccfea2204d3e69..0eb43b2372701d47b9ef62cbbdb32b97a5f7a0ba 100644
|
||||
--- a/daemons/ipa-otpd/oauth2.c
|
||||
+++ b/daemons/ipa-otpd/oauth2.c
|
||||
@@ -104,17 +104,26 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
|
||||
}
|
||||
|
||||
if (child_ctx->oauth2_state == OAUTH2_GET_DEVICE_CODE) {
|
||||
- io = write(verto_get_fd(ev), child_ctx->item->idp.ipaidpClientSecret,
|
||||
- strlen(child_ctx->item->idp.ipaidpClientSecret));
|
||||
+ if (child_ctx->item->idp.ipaidpClientSecret != NULL) {
|
||||
+ io = write(verto_get_fd(ev), child_ctx->item->idp.ipaidpClientSecret,
|
||||
+ strlen(child_ctx->item->idp.ipaidpClientSecret));
|
||||
+ } else {
|
||||
+ io = 0;
|
||||
+ }
|
||||
} else {
|
||||
- iov[0].iov_base = child_ctx->item->idp.ipaidpClientSecret;
|
||||
- iov[0].iov_len = strlen(child_ctx->item->idp.ipaidpClientSecret);
|
||||
- iov[1].iov_base = "\n";
|
||||
- iov[1].iov_len = 1;
|
||||
- iov[2].iov_base = child_ctx->saved_item->oauth2.device_code_reply;
|
||||
- iov[2].iov_len = strlen(child_ctx->saved_item->oauth2.device_code_reply);
|
||||
-
|
||||
- io = writev(verto_get_fd(ev), iov, 3);
|
||||
+ int idx = 0;
|
||||
+ if (child_ctx->item->idp.ipaidpClientSecret != NULL) {
|
||||
+ iov[idx].iov_base = child_ctx->item->idp.ipaidpClientSecret;
|
||||
+ iov[idx].iov_len = strlen(child_ctx->item->idp.ipaidpClientSecret);
|
||||
+ idx++;
|
||||
+ iov[idx].iov_base = "\n";
|
||||
+ iov[idx].iov_len = 1;
|
||||
+ idx++;
|
||||
+ }
|
||||
+ iov[idx].iov_base = child_ctx->saved_item->oauth2.device_code_reply;
|
||||
+ iov[idx].iov_len = strlen(child_ctx->saved_item->oauth2.device_code_reply);
|
||||
+ idx++;
|
||||
+ io = writev(verto_get_fd(ev), iov, idx);
|
||||
}
|
||||
otpd_queue_item_free(child_ctx->saved_item);
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,165 +0,0 @@
|
||||
From 4e43dd7cd30042588a2264fca98b6e6b9d4d25bb Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Fri, 17 Jan 2025 12:33:54 +0200
|
||||
Subject: [PATCH] Migrate Keycloak tests to JDK 21 and Keycloak 26
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
.../pytest_ipa/integration/create_bridge.py | 2 +-
|
||||
.../pytest_ipa/integration/create_keycloak.py | 28 +++++++++----------
|
||||
ipatests/test_integration/test_idp.py | 4 +--
|
||||
ipatests/test_integration/test_sso.py | 4 +--
|
||||
4 files changed, 18 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/ipatests/pytest_ipa/integration/create_bridge.py b/ipatests/pytest_ipa/integration/create_bridge.py
|
||||
index 618c645feef86f846a60e5727e7777defc67624c..5dd2f305c2ba6f707ee40be12581ff62c951805b 100644
|
||||
--- a/ipatests/pytest_ipa/integration/create_bridge.py
|
||||
+++ b/ipatests/pytest_ipa/integration/create_bridge.py
|
||||
@@ -147,7 +147,7 @@ def setup_keycloak_scim_plugin(host, bridge_server):
|
||||
# Login to keycloak as admin
|
||||
kcadmin_sh = "/opt/keycloak/bin/kcadm.sh"
|
||||
kcadmin = [kcadmin_sh, "config", "credentials", "--server",
|
||||
- f"https://{host.hostname}:8443/auth/",
|
||||
+ f"https://{host.hostname}:8443",
|
||||
"--realm", "master", "--user", "admin",
|
||||
"--password", password]
|
||||
tasks.run_repeatedly(host, kcadmin, timeout=60)
|
||||
diff --git a/ipatests/pytest_ipa/integration/create_keycloak.py b/ipatests/pytest_ipa/integration/create_keycloak.py
|
||||
index 1340b95715c25f1bf1cbbf2e3c6e60731f3af08e..addade7594d7a1b8edefdb8c67ec4bc7abe70ef4 100644
|
||||
--- a/ipatests/pytest_ipa/integration/create_keycloak.py
|
||||
+++ b/ipatests/pytest_ipa/integration/create_keycloak.py
|
||||
@@ -6,10 +6,10 @@ from ipaplatform.paths import paths
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
|
||||
|
||||
-def setup_keycloakserver(host, version='17.0.0'):
|
||||
+def setup_keycloakserver(host, version='26.1.0'):
|
||||
dir = "/opt/keycloak"
|
||||
password = host.config.admin_password
|
||||
- tasks.install_packages(host, ["unzip", "java-11-openjdk-headless",
|
||||
+ tasks.install_packages(host, ["unzip", "java-21-openjdk-headless",
|
||||
"openssl", "maven", "wget",
|
||||
"firefox", "xorg-x11-server-Xvfb"])
|
||||
# add keycloak system user/group and folder
|
||||
@@ -33,7 +33,7 @@ def setup_keycloakserver(host, version='17.0.0'):
|
||||
|
||||
key = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.key")
|
||||
crt = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.crt")
|
||||
- keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.store")
|
||||
+ keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.jks")
|
||||
|
||||
host.run_command(["ipa-getcert", "request", "-K",
|
||||
"HTTP/{0}".format(host.hostname),
|
||||
@@ -49,14 +49,13 @@ def setup_keycloakserver(host, version='17.0.0'):
|
||||
|
||||
# Setup keycloak service and config files
|
||||
contents = textwrap.dedent("""
|
||||
- KEYCLOAK_ADMIN=admin
|
||||
- KEYCLOAK_ADMIN_PASSWORD={admin_pswd}
|
||||
- KC_HOSTNAME={host}:8443
|
||||
+ KC_BOOTSTRAP_ADMIN_USERNAME=admin
|
||||
+ KC_BOOTSTRAP_ADMIN_PASSWORD={admin_pswd}
|
||||
+ KC_HOSTNAME=https://{host}:8443/
|
||||
KC_HTTPS_CERTIFICATE_FILE={crt}
|
||||
KC_HTTPS_CERTIFICATE_KEY_FILE={key}
|
||||
KC_HTTPS_TRUST_STORE_FILE={store}
|
||||
KC_HTTPS_TRUST_STORE_PASSWORD={store_pswd}
|
||||
- KC_HTTP_RELATIVE_PATH=/auth
|
||||
""").format(admin_pswd=password, host=host.hostname, crt=crt, key=key,
|
||||
store=keystore, store_pswd=password)
|
||||
host.put_file_contents("/etc/sysconfig/keycloak", contents)
|
||||
@@ -84,14 +83,13 @@ def setup_keycloakserver(host, version='17.0.0'):
|
||||
|
||||
# Run build stage first
|
||||
env_vars = textwrap.dedent("""
|
||||
- export KEYCLOAK_ADMIN=admin
|
||||
- export KC_HOSTNAME={hostname}:8443
|
||||
+ export KC_BOOTSTRAP_ADMIN_USERNAME=admin
|
||||
+ export KC_HOSTNAME=https://{hostname}:8443/
|
||||
export KC_HTTPS_CERTIFICATE_FILE=/etc/pki/tls/certs/keycloak.crt
|
||||
export KC_HTTPS_CERTIFICATE_KEY_FILE=/etc/pki/tls/private/keycloak.key
|
||||
- export KC_HTTPS_TRUST_STORE_FILE=/etc/pki/tls/private/keycloak.store
|
||||
+ export KC_HTTPS_TRUST_STORE_FILE=/etc/pki/tls/private/keycloak.jks
|
||||
export KC_HTTPS_TRUST_STORE_PASSWORD={STORE_PASS}
|
||||
- export KEYCLOAK_ADMIN_PASSWORD={ADMIN_PASS}
|
||||
- export KC_HTTP_RELATIVE_PATH=/auth
|
||||
+ export KC_BOOTSTRAP_ADMIN_PASSWORD={ADMIN_PASS}
|
||||
""").format(hostname=host.hostname, STORE_PASS=password,
|
||||
ADMIN_PASS=password)
|
||||
|
||||
@@ -112,7 +110,7 @@ def setup_keycloakserver(host, version='17.0.0'):
|
||||
host.run_command([kcadmin_sh, "config", "truststore",
|
||||
"--trustpass", password, keystore])
|
||||
kcadmin = [kcadmin_sh, "config", "credentials", "--server",
|
||||
- "https://{0}:8443/auth/".format(host.hostname),
|
||||
+ "https://{0}:8443/".format(host.hostname),
|
||||
"--realm", "master", "--user", "admin",
|
||||
"--password", password
|
||||
]
|
||||
@@ -133,7 +131,7 @@ def setup_keycloak_client(host):
|
||||
password = host.config.admin_password
|
||||
host.run_command(["/opt/keycloak/bin/kcreg.sh",
|
||||
"config", "credentials", "--server",
|
||||
- "https://{0}:8443/auth/".format(host.hostname),
|
||||
+ "https://{0}:8443/".format(host.hostname),
|
||||
"--realm", "master", "--user", "admin",
|
||||
"--password", password]
|
||||
)
|
||||
@@ -163,7 +161,7 @@ def setup_keycloak_client(host):
|
||||
def uninstall_keycloak(host):
|
||||
key = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.key")
|
||||
crt = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.crt")
|
||||
- keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.store")
|
||||
+ keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.jks")
|
||||
|
||||
host.run_command(["systemctl", "stop", "keycloak"], raiseonerr=False)
|
||||
host.run_command(["getcert", "stop-tracking", "-k", key, "-f", crt],
|
||||
diff --git a/ipatests/test_integration/test_idp.py b/ipatests/test_integration/test_idp.py
|
||||
index ca2fcecb22459685450f2ed6c3ac1b9b215170f6..76edc9458e4448e05362ff040b8dab7a53cd3054 100644
|
||||
--- a/ipatests/test_integration/test_idp.py
|
||||
+++ b/ipatests/test_integration/test_idp.py
|
||||
@@ -122,7 +122,7 @@ class TestIDPKeycloak(IntegrationTest):
|
||||
tasks.kinit_admin(self.master)
|
||||
cmd = ["ipa", "idp-add", "keycloakidp", "--provider=keycloak",
|
||||
"--client-id=ipa_oidc_client", "--org=master",
|
||||
- "--base-url={0}:8443/auth".format(self.client.hostname)]
|
||||
+ "--base-url={0}:8443".format(self.client.hostname)]
|
||||
self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
|
||||
self.client.config.admin_password))
|
||||
tasks.user_add(self.master, 'keycloakuser',
|
||||
@@ -282,7 +282,7 @@ class TestIDPKeycloak(IntegrationTest):
|
||||
user = "backupuser"
|
||||
cmd = ["ipa", "idp-add", "testidp", "--provider=keycloak",
|
||||
"--client-id=ipa_oidc_client", "--org=master",
|
||||
- "--base-url={0}:8443/auth".format(self.client.hostname)]
|
||||
+ "--base-url={0}:8443".format(self.client.hostname)]
|
||||
self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
|
||||
self.client.config.admin_password))
|
||||
|
||||
diff --git a/ipatests/test_integration/test_sso.py b/ipatests/test_integration/test_sso.py
|
||||
index 9708e9fa05a75cb2657c657b39b015249f3fd208..57c5a96bae986ee9721fc540d2be2cdc443e78fb 100644
|
||||
--- a/ipatests/test_integration/test_sso.py
|
||||
+++ b/ipatests/test_integration/test_sso.py
|
||||
@@ -18,7 +18,7 @@ from selenium.webdriver.support import expected_conditions as EC
|
||||
options = Options()
|
||||
options.headless = True
|
||||
driver = webdriver.Firefox(executable_path="/opt/geckodriver", options=options)
|
||||
-verification_uri = "https://{hostname}:8443/auth/realms/master/account/#/"
|
||||
+verification_uri = "https://{hostname}:8443/realms/master/account/#/"
|
||||
driver.get(verification_uri)
|
||||
|
||||
try:
|
||||
@@ -60,7 +60,7 @@ def keycloak_add_user(host, kcadm_pass, username, password=None):
|
||||
domain = host.domain.name
|
||||
kcadmin_sh = "/opt/keycloak/bin/kcadm.sh"
|
||||
kcadmin = [kcadmin_sh, "config", "credentials", "--server",
|
||||
- f"https://{host.hostname}:8443/auth/",
|
||||
+ f"https://{host.hostname}:8443",
|
||||
"--realm", "master", "--user", "admin",
|
||||
"--password", kcadm_pass]
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
||||
@ -1,162 +0,0 @@
|
||||
From 9f30edef463237ba48efe45406626eb325bf6c39 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Wed, 22 Jan 2025 13:19:43 -0500
|
||||
Subject: [PATCH] Apply certmonger_timeout to start_tracking and request_cert
|
||||
|
||||
We've seen that with some slow HSMs the default DBus timeout
|
||||
the HSM doesn't respond quickly enough to certmonger start
|
||||
tracking requests which fails the entire installation.
|
||||
|
||||
A first attempt was made to bump up the default to 30 seconds
|
||||
which turned out to not be long enough.
|
||||
|
||||
There is already a certmonger timeout defined in the API but it
|
||||
is 300 seconds so I was hesitant to use it. It could delay the
|
||||
actual failure of a blown install by 5 minutes. But it also gives
|
||||
the end user the flexibility to be able to control success over
|
||||
an installation so we'll go ahead and use it.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9725
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Mohammad Rizwan Yusuf <myusuf@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
client/man/default.conf.5 | 10 ++++++++--
|
||||
ipalib/install/certmonger.py | 7 +++++--
|
||||
ipaserver/install/cainstance.py | 5 ++++-
|
||||
ipaserver/install/dogtaginstance.py | 18 ++++++++++++++++++
|
||||
ipaserver/install/service.py | 4 +++-
|
||||
5 files changed, 38 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/client/man/default.conf.5 b/client/man/default.conf.5
|
||||
index 3846de50c5d851471ea3ceed9fc38cb687c719e4..e0aec21f725d88ce2ba3cf52901fb15575892cde 100644
|
||||
--- a/client/man/default.conf.5
|
||||
+++ b/client/man/default.conf.5
|
||||
@@ -20,7 +20,7 @@
|
||||
.SH "NAME"
|
||||
default.conf \- IPA configuration file
|
||||
.SH "SYNOPSIS"
|
||||
-/etc/ipa/default.conf, ~/.ipa/default.conf, /etc/ipa/server.conf, /etc/ipa/cli.conf
|
||||
+/etc/ipa/default.conf, ~/.ipa/default.conf, /etc/ipa/server.conf, /etc/ipa/cli.conf, /etc/ipa/installer.conf, /etc/ipa/cli_installer.conf
|
||||
.SH "DESCRIPTION"
|
||||
The \fIdefault.conf \fRconfiguration file is used to set system\-wide defaults to be applied when running IPA clients and servers.
|
||||
|
||||
@@ -75,7 +75,7 @@ Specifies the hostname of the dogtag CA server. The default is the hostname of t
|
||||
Specifies the insecure CA end user port. The default is 8080.
|
||||
.TP
|
||||
.B certmonger_wait_timeout <seconds>
|
||||
-The time to wait for a certmonger request to complete during installation. The default value is 300 seconds.
|
||||
+The time to wait for a certmonger request to complete during installation. The default value is 300 seconds. To tune create/add to /etc/ipa/installer.conf.
|
||||
.TP
|
||||
.B context <context>
|
||||
Specifies the context that IPA is being executed in. IPA may operate differently depending on the context. The current defined contexts are cli, server and dns. Additionally this value is used to load /etc/ipa/\fBcontext\fR.conf to provide context\-specific configuration. For example, if you want to always perform client requests in verbose mode but do not want to have verbose enabled on the server, add the verbose option to \fI/etc/ipa/cli.conf\fR.
|
||||
@@ -263,6 +263,12 @@ system\-wide IPA client configuration file
|
||||
.TP
|
||||
.I /etc/ipa/server.conf
|
||||
system\-wide IPA server configuration file
|
||||
+.TP
|
||||
+.I /etc/ipa/installer.conf
|
||||
+IPA configuration used while installing an IPA server or replica
|
||||
+.TP
|
||||
+.I /etc/ipa/cli_installer.conf
|
||||
+IPA configuration used while installing an IPA client
|
||||
.SH "EXAMPLES"
|
||||
.TP
|
||||
An example of a context-specific configuration file is \fB/etc/ipa/dns.conf\fR to be used to increase debug output of the IPA DNSSEC daemons.
|
||||
diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py
|
||||
index efc1ba4f42eab98df5fac51bafa3acc83ae91831..675b2c96ce8ebe4f06822ad587a4bca734a1be09 100644
|
||||
--- a/ipalib/install/certmonger.py
|
||||
+++ b/ipalib/install/certmonger.py
|
||||
@@ -477,7 +477,8 @@ def request_cert(
|
||||
request_parameters['cert-perms'] = perms[0]
|
||||
request_parameters['key-perms'] = perms[1]
|
||||
|
||||
- result = cm.obj_if.add_request(request_parameters, timeout=30)
|
||||
+ result = cm.obj_if.add_request(request_parameters,
|
||||
+ timeout=api.env.certmonger_wait_timeout)
|
||||
try:
|
||||
if result[0]:
|
||||
request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
|
||||
@@ -581,7 +582,9 @@ def start_tracking(
|
||||
if nss_user:
|
||||
params['nss-user'] = nss_user
|
||||
|
||||
- result = cm.obj_if.add_request(params, timeout=30)
|
||||
+ logger.debug("start tracking %s", params)
|
||||
+ result = cm.obj_if.add_request(params,
|
||||
+ timeout=api.env.certmonger_wait_timeout)
|
||||
try:
|
||||
if result[0]:
|
||||
request = _cm_dbus_object(cm.bus, cm, result[1], DBUS_CM_REQUEST_IF,
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index e03a8c863e14782679e19c6887f5e220131e4234..76718036dbd317651edc98ce631405e42bf814d7 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -513,7 +513,10 @@ class CAInstance(DogtagInstance):
|
||||
if ra_only:
|
||||
runtime = None
|
||||
else:
|
||||
- runtime = 180
|
||||
+ if self.tokenname:
|
||||
+ runtime = "HSM dependent"
|
||||
+ else:
|
||||
+ runtime = 180
|
||||
|
||||
try:
|
||||
self.start_creation(runtime=runtime)
|
||||
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
|
||||
index 32a52dbedaa34b24c2658460f0ae889e7a37aa64..002053ed797beec829d324e80fc55b57cabf04be 100644
|
||||
--- a/ipaserver/install/dogtaginstance.py
|
||||
+++ b/ipaserver/install/dogtaginstance.py
|
||||
@@ -575,6 +575,24 @@ class DogtagInstance(service.Service):
|
||||
except RuntimeError as e:
|
||||
logger.error(
|
||||
"certmonger failed to start tracking certificate: %s", e)
|
||||
+ except dbus.exceptions.DBusException as e:
|
||||
+ if e._dbus_error_name == "org.freedesktop.DBus.Error.NoReply":
|
||||
+ logger.error(
|
||||
+ "Timeout encountered starting tracking of '%s'."
|
||||
+ "This timeout can be tuned using "
|
||||
+ "certmonger_wait_timeout in /etc/ipa/installer.conf.",
|
||||
+ nickname
|
||||
+ )
|
||||
+ if self.hsm_enabled:
|
||||
+ logger.error(
|
||||
+ "On an initial install failure this may leave "
|
||||
+ "certificates and keys on the HSM token. These "
|
||||
+ "need to be manually cleaned per your HSM-specific "
|
||||
+ "documentation before installing IPA again. On a "
|
||||
+ "replica install no clean-up should be done (it will "
|
||||
+ "destroy your installation."
|
||||
+ )
|
||||
+ raise
|
||||
|
||||
def stop_tracking_certificates(self):
|
||||
"""
|
||||
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
|
||||
index cf0f64ab9794111761adf735bc488269bd1814fc..7755a4f2ff5e33e61f85dc24b71fd05a1837cd5a 100644
|
||||
--- a/ipaserver/install/service.py
|
||||
+++ b/ipaserver/install/service.py
|
||||
@@ -59,6 +59,8 @@ def print_msg(message, output_fd=sys.stdout):
|
||||
|
||||
def format_seconds(seconds):
|
||||
"""Format a number of seconds as an English minutes+seconds message"""
|
||||
+ if type(seconds) is not int:
|
||||
+ return seconds
|
||||
parts = []
|
||||
minutes, seconds = divmod(seconds, 60)
|
||||
if minutes:
|
||||
@@ -660,7 +662,7 @@ class Service:
|
||||
else:
|
||||
end_message = "Done configuring %s." % self.service_desc
|
||||
|
||||
- if runtime is not None and runtime > 0:
|
||||
+ if runtime is not None or (type(runtime) is int and runtime > 0):
|
||||
self.print_msg('%s. Estimated time: %s' % (start_message,
|
||||
format_seconds(runtime)))
|
||||
else:
|
||||
--
|
||||
2.48.1
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,55 +0,0 @@
|
||||
From 47ce0982249ee7ce12b38eae5ce3ee6a9b5df52e Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Tue, 4 Feb 2025 12:54:48 -0500
|
||||
Subject: [PATCH] Configure the pki-tomcatd service systemd timeout
|
||||
|
||||
IPA defines a startup timeout that is primarily used
|
||||
during installation to extend service start-up timeouts
|
||||
on slower systems.
|
||||
|
||||
This tends to work ok when runing pki-spawn but can fail when
|
||||
systemd is starting the tomcat service.
|
||||
|
||||
Use the value of startup_timeout to set TimeoutStartSec in
|
||||
the pki-tomcat systemd override file ipa.conf. This will
|
||||
preserve the necessary startup_timeout for all future restarts.
|
||||
|
||||
This was seen with a very slow HSM where installation was successful
|
||||
(pki-spawn) but pki-tomcatd startup timed out at the end of the
|
||||
installation.
|
||||
|
||||
To increase the value in installation one needs to create the file
|
||||
/etc/ipa/installer.conf with contents:
|
||||
|
||||
[global]
|
||||
startup_timeout = 300 (or whatever)
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9743
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipaserver/install/cainstance.py | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index 76718036dbd317651edc98ce631405e42bf814d7..c8ecde8f2e9649d57012fcda937ee5816105df4e 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -713,7 +713,12 @@ class CAInstance(DogtagInstance):
|
||||
f.write('[Service]\n')
|
||||
f.write('Environment=LC_ALL=C.UTF-8\n')
|
||||
f.write('ExecStartPost={}\n'.format(paths.IPA_PKI_WAIT_RUNNING))
|
||||
+ f.write('TimeoutStartSec=%d\n' % api.env.startup_timeout)
|
||||
tasks.systemd_daemon_reload()
|
||||
+ logger.info(
|
||||
+ "Set start up timeout of pki-tomcatd service to %d seconds",
|
||||
+ api.env.startup_timeout
|
||||
+ )
|
||||
|
||||
def safe_backup_config(self):
|
||||
"""
|
||||
--
|
||||
2.48.1
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
From 22cbc5ed4889d6c66e2916d5acde582b1868fbc9 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Mon, 10 Feb 2025 10:45:39 -0500
|
||||
Subject: [PATCH] Align startup_timeout with the systemd default and document
|
||||
it
|
||||
|
||||
We had it set to 120 seconds while the systemd default is 90.
|
||||
They should be the same because the first one that times out "wins".
|
||||
|
||||
Move where during the installation we create the systemd override
|
||||
file so that the timeout will be applied across all subsequent
|
||||
server starts during and post installation.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9743
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
client/man/default.conf.5 | 2 +-
|
||||
ipalib/constants.py | 5 +++--
|
||||
ipaserver/install/cainstance.py | 3 ++-
|
||||
3 files changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/client/man/default.conf.5 b/client/man/default.conf.5
|
||||
index e0aec21f725d88ce2ba3cf52901fb15575892cde..461c60134124ed3e31e17ac350576487fda4c46e 100644
|
||||
--- a/client/man/default.conf.5
|
||||
+++ b/client/man/default.conf.5
|
||||
@@ -191,7 +191,7 @@ Specifies the IPA Server hostname.
|
||||
Skip client vs. server API version checking. Can lead to errors/strange behavior when newer clients talk to older servers. Use with caution.
|
||||
.TP
|
||||
.B startup_timeout <time in seconds>
|
||||
-Controls the amount of time waited when starting a service. The default value is 120 seconds.
|
||||
+Controls the amount of time waited when starting a service. The default value is 90 seconds, the same as the default systemd startup timeout. If configuring a CA the startup_timeout value will be added as an override for TimeoutStartSec in systemd. If installation times out when starting the CA create /etc/ipa/installer.conf with this value set.
|
||||
.TP
|
||||
.B startup_traceback <boolean>
|
||||
If the IPA server fails to start and this value is True the server will attempt to generate a python traceback to make identifying the underlying problem easier.
|
||||
diff --git a/ipalib/constants.py b/ipalib/constants.py
|
||||
index c90caa22149ec3d93d45fcb5480f7401e4555799..2e4c9a8336efae9e02febd6d04ec226c84af255f 100644
|
||||
--- a/ipalib/constants.py
|
||||
+++ b/ipalib/constants.py
|
||||
@@ -189,8 +189,9 @@ DEFAULT_CONFIG = (
|
||||
|
||||
# Time to wait for a service to start, in seconds.
|
||||
# Note that systemd has a DefaultTimeoutStartSec of 90 seconds. Higher
|
||||
- # values are not effective unless systemd is reconfigured, too.
|
||||
- ('startup_timeout', 120),
|
||||
+ # values are not effective unless systemd is reconfigured, too. Or you
|
||||
+ # can update the systemd service file with its own TimeoutStartSec.
|
||||
+ ('startup_timeout', 90),
|
||||
# How long http connection should wait for reply [seconds].
|
||||
('http_timeout', 30),
|
||||
# How long to wait for an entry to appear on a replica
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index c8ecde8f2e9649d57012fcda937ee5816105df4e..3466c308829a576589874015542da9ea88bc2a2f 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -453,6 +453,7 @@ class CAInstance(DogtagInstance):
|
||||
if promote:
|
||||
self.step("destroying installation admin user",
|
||||
self.teardown_admin)
|
||||
+ self.step("updating IPA configuration", update_ipa_conf)
|
||||
# Materialize config changes and new ACLs
|
||||
self.step("starting certificate server instance",
|
||||
self.start_instance)
|
||||
@@ -480,7 +481,6 @@ class CAInstance(DogtagInstance):
|
||||
self.step("configure certificate renewals", self.configure_renewal)
|
||||
self.step("Configure HTTP to proxy connections",
|
||||
self.http_proxy)
|
||||
- self.step("updating IPA configuration", update_ipa_conf)
|
||||
self.step("enabling CA instance", self.__enable_instance)
|
||||
if not promote:
|
||||
if self.clone:
|
||||
@@ -2453,6 +2453,7 @@ def update_ipa_conf(ca_host=None):
|
||||
parser.set('global', 'enable_ra', 'True')
|
||||
parser.set('global', 'ra_plugin', 'dogtag')
|
||||
parser.set('global', 'dogtag_version', '10')
|
||||
+ parser.set('global', 'startup_timeout', api.env.startup_timeout)
|
||||
if ca_host is None:
|
||||
parser.remove_option('global', 'ca_host')
|
||||
else:
|
||||
--
|
||||
2.48.1
|
||||
|
||||
@ -1,62 +0,0 @@
|
||||
From 91353b10748f1153540c6f5447a80864dee59d7f Mon Sep 17 00:00:00 2001
|
||||
From: Antonio Torres <antorres@redhat.com>
|
||||
Date: Wed, 12 Feb 2025 09:48:58 +0100
|
||||
Subject: [PATCH] dns: only disable unbound when DoT is enabled
|
||||
|
||||
Ensure unbound is only stopped and disabled when DNS over TLS was
|
||||
enabled during installation.
|
||||
|
||||
Signed-off-by: Antonio Torres <antorres@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/bindinstance.py | 11 +++++++----
|
||||
ipaserver/install/dns.py | 3 +++
|
||||
2 files changed, 10 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
|
||||
index 4f4ab9bbc66fcfd89354d6659cf9ac2bcaa270f6..0cc1f1325ce0a9dbdb09f4100a1a22bc4f24924a 100644
|
||||
--- a/ipaserver/install/bindinstance.py
|
||||
+++ b/ipaserver/install/bindinstance.py
|
||||
@@ -689,6 +689,8 @@ class BindInstance(service.Service):
|
||||
self.forward_policy = forward_policy
|
||||
self.reverse_zones = reverse_zones
|
||||
|
||||
+ self.sstore.backup_state("dns_over_tls", "enabled", dns_over_tls)
|
||||
+
|
||||
if not zonemgr:
|
||||
self.zonemgr = 'hostmaster.%s' % normalize_zone(self.domain)
|
||||
else:
|
||||
@@ -1377,10 +1379,11 @@ class BindInstance(service.Service):
|
||||
|
||||
self.named_conflict.unmask()
|
||||
|
||||
- certmonger.stop_tracking(certfile=paths.BIND_DNS_OVER_TLS_CRT)
|
||||
- certmonger.stop_tracking(certfile=paths.BIND_DNS_OVER_TLS_KEY)
|
||||
- services.knownservices.unbound.disable()
|
||||
- services.knownservices.unbound.stop()
|
||||
+ if self.sstore.restore_state("dns_over_tls", "enabled"):
|
||||
+ if not self.sstore.restore_state("dns_over_tls", "external_crt"):
|
||||
+ certmonger.stop_tracking(certfile=paths.BIND_DNS_OVER_TLS_CRT)
|
||||
+ services.knownservices["unbound"].disable()
|
||||
+ services.knownservices["unbound"].stop()
|
||||
|
||||
ipautil.remove_file(paths.NAMED_CONF_BAK)
|
||||
ipautil.remove_file(paths.NAMED_CUSTOM_CONF)
|
||||
diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py
|
||||
index 29ca0d2ff4efa8ad80784b393f49ac8ec0e03512..88aff19bcec11f778af5644167c32c45cbcab594 100644
|
||||
--- a/ipaserver/install/dns.py
|
||||
+++ b/ipaserver/install/dns.py
|
||||
@@ -457,6 +457,9 @@ def install(standalone, replica, options, api=api):
|
||||
|
||||
bind.create_instance()
|
||||
|
||||
+ bind.sstore.backup_state("dns_over_tls",
|
||||
+ "external_crt",
|
||||
+ bool(options.dns_over_tls_cert))
|
||||
if options.dns_over_tls:
|
||||
print("Setting up DNS over TLS")
|
||||
_setup_dns_over_tls(options)
|
||||
--
|
||||
2.48.1
|
||||
|
||||
@ -1,66 +0,0 @@
|
||||
From 7fd4b940abd2084fd6ec7de73dfd68551fce73fe Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 29 Jan 2025 10:07:45 -0500
|
||||
Subject: [PATCH] ipa-migrate - do not migrate tombstone entries, ignore
|
||||
MidairCollisions, and krbpwdpolicyreference
|
||||
|
||||
Replication related entries should not be migrated. The main reason is
|
||||
that we do not allow entries to be added that have an RDN of nsuniqueid
|
||||
(only the server can internally add them).
|
||||
|
||||
Most midair collisions are transient issues and can be ignored for
|
||||
migration purposes. In migration tests this only happens when an
|
||||
attribute does not exist in the local server. This happens frequently
|
||||
with COS attributes.
|
||||
|
||||
We should also ignore 'krbpwdpolicyreference' as it's an attribute that is
|
||||
set by COS and does not need to be migrated.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9737
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/ipa_migrate.py | 8 ++++++++
|
||||
ipaserver/install/ipa_migrate_constants.py | 1 +
|
||||
2 files changed, 9 insertions(+)
|
||||
|
||||
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
|
||||
index ece473bc8cb525e2d563356b5b274502d6b703e8..5ba140ce37156a6f2cb50d08427f5024925686e6 100644
|
||||
--- a/ipaserver/install/ipa_migrate.py
|
||||
+++ b/ipaserver/install/ipa_migrate.py
|
||||
@@ -1462,6 +1462,10 @@ class IPAMigrate():
|
||||
if DN(exclude_dn) in DN(entry_dn):
|
||||
return
|
||||
|
||||
+ # Skip tombstones
|
||||
+ if 'nsTombstone' in entry_attrs['objectClass']:
|
||||
+ return
|
||||
+
|
||||
# Determine entry type: user, group, hbac, etc
|
||||
entry_type = self.get_entry_type(entry_dn, entry_attrs)
|
||||
if entry_type is None:
|
||||
@@ -1568,6 +1572,10 @@ class IPAMigrate():
|
||||
stats['custom'] += 1
|
||||
else:
|
||||
DB_OBJECTS[entry_type]['count'] += 1
|
||||
+ except errors.MidairCollision as e:
|
||||
+ # Typically means no such attribute, ok to ignore
|
||||
+ self.log_debug(f'Failed to update "{local_dn}" error: '
|
||||
+ f'{str(e)} - ok to ignore')
|
||||
except errors.ExecutionError as e:
|
||||
self.log_error(f'Failed to update "{local_dn}" error: '
|
||||
f'{str(e)}')
|
||||
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
|
||||
index e8192fb1aabae1c36669370eff242428a1f0355f..09856f07cabd124a7899bc5f355a56eb23023cc0 100644
|
||||
--- a/ipaserver/install/ipa_migrate_constants.py
|
||||
+++ b/ipaserver/install/ipa_migrate_constants.py
|
||||
@@ -71,6 +71,7 @@ IGNORE_ATTRS = [
|
||||
'serverhostname',
|
||||
'krbpasswordexpiration',
|
||||
'krblastadminunlock',
|
||||
+ 'krbpwdpolicyreference', # COS attribute
|
||||
]
|
||||
|
||||
# For production mode, bring everything over
|
||||
--
|
||||
2.48.1
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
From 100737fff5a0039cd883a92400d1495dd5bf7658 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Fri, 7 Mar 2025 09:01:35 +0100
|
||||
Subject: [PATCH] WebUI: fix the tooltip for Search Size limit
|
||||
|
||||
The tooltip for IPA Server > Configuration > Search size limit
|
||||
is using the doc from ipasearchtimelimit instead of
|
||||
ipasearchrecordslimit.
|
||||
|
||||
Use the right tooltip to properly display:
|
||||
Maximum number of records to search (-1 or 0 is unlimited)
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9758
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
install/ui/src/freeipa/serverconfig.js | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/install/ui/src/freeipa/serverconfig.js b/install/ui/src/freeipa/serverconfig.js
|
||||
index e81e48cfb405c4ccc2591edb754fa88f5586c8e0..d7f67e885bdb2c884a5dedd615bade5fcedc863d 100644
|
||||
--- a/install/ui/src/freeipa/serverconfig.js
|
||||
+++ b/install/ui/src/freeipa/serverconfig.js
|
||||
@@ -47,7 +47,7 @@ return {
|
||||
fields: [
|
||||
{
|
||||
name: 'ipasearchrecordslimit',
|
||||
- tooltip: '@mc-opt:config_mod:ipasearchtimelimit:doc'
|
||||
+ tooltip: '@mc-opt:config_mod:ipasearchrecordslimit:doc'
|
||||
},
|
||||
{
|
||||
name: 'ipasearchtimelimit',
|
||||
--
|
||||
2.48.1
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
From 9b566fe458fb36eb5eb3212b01bc6ba48ac8349a Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 11 Mar 2025 15:55:11 +0100
|
||||
Subject: [PATCH] Leapp upgrade: skip systemctl calls
|
||||
|
||||
During LEAPP upgrade, the system is booted in a special mode
|
||||
without systemd. As a consequence, any scriptlet calling
|
||||
systemctl fails and may break the upgrade.
|
||||
|
||||
Skip the call to systemctl if a LEAPP upgrade is in progress
|
||||
(this is easily checked using the env variable $LEAPP_IPU_IN_PROGRESS
|
||||
that is set for instance to LEAPP_IPU_IN_PROGRESS=8to9).
|
||||
|
||||
Fixes: RHEL-82089
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
freeipa.spec.in | 14 ++++++++++----
|
||||
1 file changed, 10 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||
index b539f51f88a19a3686684dd0a9138add97bbd285..143ee5c83d16b59531feda011c087c0ab4c82786 100755
|
||||
--- a/freeipa.spec.in
|
||||
+++ b/freeipa.spec.in
|
||||
@@ -1241,8 +1241,11 @@ if [ $1 = 0 ]; then
|
||||
# NOTE: systemd specific section
|
||||
/bin/systemctl --quiet stop ipa.service || :
|
||||
/bin/systemctl --quiet disable ipa.service || :
|
||||
- /bin/systemctl reload-or-try-restart dbus
|
||||
- /bin/systemctl reload-or-try-restart oddjobd
|
||||
+ # Skip systemctl calls when leapp upgrade is in progress
|
||||
+ if [ -z "$LEAPP_IPU_IN_PROGRESS" ] ; then
|
||||
+ /bin/systemctl reload-or-try-restart dbus
|
||||
+ /bin/systemctl reload-or-try-restart oddjobd
|
||||
+ fi
|
||||
# END
|
||||
fi
|
||||
|
||||
@@ -1306,8 +1309,11 @@ fi
|
||||
%preun server-trust-ad
|
||||
if [ $1 -eq 0 ]; then
|
||||
%{_sbindir}/update-alternatives --remove winbind_krb5_locator.so /dev/null
|
||||
- /bin/systemctl reload-or-try-restart dbus
|
||||
- /bin/systemctl reload-or-try-restart oddjobd
|
||||
+ # Skip systemctl calls when leapp upgrade is in progress
|
||||
+ if [ -z "$LEAPP_IPU_IN_PROGRESS" ] ; then
|
||||
+ /bin/systemctl reload-or-try-restart dbus
|
||||
+ /bin/systemctl reload-or-try-restart oddjobd
|
||||
+ fi
|
||||
fi
|
||||
|
||||
# ONLY_CLIENT
|
||||
--
|
||||
2.48.1
|
||||
|
||||
@ -1,355 +0,0 @@
|
||||
From 653b4b6971b1778988718840a301c10b3e35e700 Mon Sep 17 00:00:00 2001
|
||||
From: David Hanina <dhanina@redhat.com>
|
||||
Date: Thu, 6 Mar 2025 09:32:01 +0100
|
||||
Subject: [PATCH] Disable --raw and --structured together
|
||||
|
||||
Disables --raw and --structured for dnsrecord-* command.
|
||||
This is being shown in help for structured, as raw is implemented in
|
||||
almost every command, therefore people are more likely to view
|
||||
structured. Also contains tests, even though this is newly noted, this
|
||||
combination has never worked in the past.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9756
|
||||
Signed-off-by: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaclient/remote_plugins/2_114/dns.py | 15 +++++++----
|
||||
ipaclient/remote_plugins/2_156/dns.py | 15 +++++++----
|
||||
ipaclient/remote_plugins/2_164/dns.py | 15 +++++++----
|
||||
ipaclient/remote_plugins/2_49/dns.py | 15 +++++++----
|
||||
ipaserver/plugins/dns.py | 28 ++++++++++++++++++++
|
||||
ipatests/test_xmlrpc/test_dns_plugin.py | 35 +++++++++++++++++++++++++
|
||||
6 files changed, 103 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/ipaclient/remote_plugins/2_114/dns.py b/ipaclient/remote_plugins/2_114/dns.py
|
||||
index 6260420008e3371dc95317d67d2f37a46b4d5d42..2f414927bad2f0838bec42bab734d3a42e87005f 100644
|
||||
--- a/ipaclient/remote_plugins/2_114/dns.py
|
||||
+++ b/ipaclient/remote_plugins/2_114/dns.py
|
||||
@@ -2625,7 +2625,8 @@ class dnsrecord_add(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -2991,7 +2992,8 @@ class dnsrecord_del(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -3405,7 +3407,8 @@ class dnsrecord_find(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -4290,7 +4293,8 @@ class dnsrecord_mod(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -4363,7 +4367,8 @@ class dnsrecord_show(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
diff --git a/ipaclient/remote_plugins/2_156/dns.py b/ipaclient/remote_plugins/2_156/dns.py
|
||||
index 4ebad93e79d38c1171b066cc5a1a0b8d6fce64b2..9ce8a7eef99eff7592f8550d0000506cc2d7824c 100644
|
||||
--- a/ipaclient/remote_plugins/2_156/dns.py
|
||||
+++ b/ipaclient/remote_plugins/2_156/dns.py
|
||||
@@ -2540,7 +2540,8 @@ class dnsrecord_add(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -2861,7 +2862,8 @@ class dnsrecord_del(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -3230,7 +3232,8 @@ class dnsrecord_find(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -4065,7 +4068,8 @@ class dnsrecord_mod(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -4138,7 +4142,8 @@ class dnsrecord_show(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
diff --git a/ipaclient/remote_plugins/2_164/dns.py b/ipaclient/remote_plugins/2_164/dns.py
|
||||
index f5adb4d54e8501b6b4efed06404ff299aa918cfb..284ef2cdaa757341db4eed044be3bb051db83d99 100644
|
||||
--- a/ipaclient/remote_plugins/2_164/dns.py
|
||||
+++ b/ipaclient/remote_plugins/2_164/dns.py
|
||||
@@ -2548,7 +2548,8 @@ class dnsrecord_add(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -2869,7 +2870,8 @@ class dnsrecord_del(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -3238,7 +3240,8 @@ class dnsrecord_find(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -4073,7 +4076,8 @@ class dnsrecord_mod(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -4146,7 +4150,8 @@ class dnsrecord_show(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
diff --git a/ipaclient/remote_plugins/2_49/dns.py b/ipaclient/remote_plugins/2_49/dns.py
|
||||
index 4b543a2c2539f7b67467b0a38ab8013a1ebe0840..1610f4af18ee46bc7304839ede2d587d61c6d0e2 100644
|
||||
--- a/ipaclient/remote_plugins/2_49/dns.py
|
||||
+++ b/ipaclient/remote_plugins/2_49/dns.py
|
||||
@@ -2233,7 +2233,8 @@ class dnsrecord_add(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -2594,7 +2595,8 @@ class dnsrecord_del(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -3013,7 +3015,8 @@ class dnsrecord_find(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -4025,7 +4028,8 @@ class dnsrecord_mod(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
@@ -4094,7 +4098,8 @@ class dnsrecord_show(Method):
|
||||
parameters.Flag(
|
||||
'structured',
|
||||
label=_(u'Structured'),
|
||||
- doc=_(u'Parse all raw DNS records and return them in a structured way'),
|
||||
+ doc=_(u'Parse all raw DNS records and return them in a '
|
||||
+ u'structured way. Can not be used with --raw.'),
|
||||
default=False,
|
||||
autofill=True,
|
||||
),
|
||||
diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py
|
||||
index 0d6260cd6c4edb8c1a9d7ac8927b7595588fae58..ff2d3ff8a7c2839645c9906300cba0d399f2325a 100644
|
||||
--- a/ipaserver/plugins/dns.py
|
||||
+++ b/ipaserver/plugins/dns.py
|
||||
@@ -3587,6 +3587,12 @@ class dnsrecord_add(LDAPCreate):
|
||||
|
||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
+
|
||||
+ if options.get('structured') and options.get('raw'):
|
||||
+ raise errors.MutuallyExclusiveError(
|
||||
+ reason=_("cannot use structured together with raw")
|
||||
+ )
|
||||
+
|
||||
precallback_attrs = []
|
||||
processed_attrs = []
|
||||
for option, option_val in options.items():
|
||||
@@ -3729,6 +3735,12 @@ class dnsrecord_mod(LDAPUpdate):
|
||||
|
||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
+
|
||||
+ if options.get('structured') and options.get('raw'):
|
||||
+ raise errors.MutuallyExclusiveError(
|
||||
+ reason=_("cannot use structured together with raw")
|
||||
+ )
|
||||
+
|
||||
if options.get('rename') and self.obj.is_pkey_zone_record(*keys):
|
||||
# zone rename is not allowed
|
||||
raise errors.ValidationError(name='rename',
|
||||
@@ -3883,6 +3895,7 @@ class dnsrecord_del(LDAPUpdate):
|
||||
|
||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
+
|
||||
try:
|
||||
old_entry = ldap.get_entry(dn, _record_attributes)
|
||||
except errors.NotFound:
|
||||
@@ -3983,6 +3996,16 @@ class dnsrecord_show(LDAPRetrieve):
|
||||
dnsrecord.structured_flag,
|
||||
)
|
||||
|
||||
+ def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
|
||||
+ assert isinstance(dn, DN)
|
||||
+
|
||||
+ if options.get('structured') and options.get('raw'):
|
||||
+ raise errors.MutuallyExclusiveError(
|
||||
+ reason=_("cannot use structured together with raw")
|
||||
+ )
|
||||
+
|
||||
+ return dn
|
||||
+
|
||||
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
if self.obj.is_pkey_zone_record(*keys):
|
||||
@@ -4013,6 +4036,11 @@ class dnsrecord_find(LDAPSearch):
|
||||
dnszoneidnsname, *args, **options):
|
||||
assert isinstance(base_dn, DN)
|
||||
|
||||
+ if options.get('structured') and options.get('raw'):
|
||||
+ raise errors.MutuallyExclusiveError(
|
||||
+ reason=_("cannot use structured together with raw")
|
||||
+ )
|
||||
+
|
||||
# validate if zone is master zone
|
||||
self.obj.check_zone(dnszoneidnsname, **options)
|
||||
|
||||
diff --git a/ipatests/test_xmlrpc/test_dns_plugin.py b/ipatests/test_xmlrpc/test_dns_plugin.py
|
||||
index 39d42e306d12c4f6623a1ed657aeac3d3bfa3e22..803b0a9571c2888dd02c4595c68403f37be7fed7 100644
|
||||
--- a/ipatests/test_xmlrpc/test_dns_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_dns_plugin.py
|
||||
@@ -3426,6 +3426,41 @@ class test_dns(Declarative):
|
||||
},
|
||||
),
|
||||
|
||||
+ dict(
|
||||
+ desc="Ensure --raw and --structure does not work "
|
||||
+ "for ipa dnsrecord-add",
|
||||
+ command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
|
||||
+ expected=errors.MutuallyExclusiveError(
|
||||
+ reason=u"cannot use structured together with raw"
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ dict(
|
||||
+ desc="Ensure --raw and --structure does not work "
|
||||
+ "for ipa dnsrecord-mod",
|
||||
+ command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
|
||||
+ expected=errors.MutuallyExclusiveError(
|
||||
+ reason=u"cannot use structured together with raw"
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ dict(
|
||||
+ desc="Ensure --raw and --structure does not work "
|
||||
+ "for ipa dnsrecord-show",
|
||||
+ command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
|
||||
+ expected=errors.MutuallyExclusiveError(
|
||||
+ reason=u"cannot use structured together with raw"
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+ dict(
|
||||
+ desc="Ensure --raw and --structure does not work "
|
||||
+ "for ipa dnsrecord-find",
|
||||
+ command=('dnrecord_add', [], {u'raw': True, u'structured': True}),
|
||||
+ expected=errors.MutuallyExclusiveError(
|
||||
+ reason=u"cannot use structured together with raw"
|
||||
+ ),
|
||||
+ ),
|
||||
]
|
||||
|
||||
|
||||
--
|
||||
2.48.1
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user