36 Commits
Author SHA1 Message Date
zpericic 0b4badd451 ipaserver: Use apache VirtualHost for IPA UI 2022-08-17 08:43:12 +02:00
zpericic e2d7602c1d ipaserver: Put WSGI config to ipa-wsgi.conf 2022-08-17 08:43:12 +02:00
Florence Blanc-Renaud a7369944d8 gitignore: add install/oddjob/org.freeipa.server.config-enable-sid
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Stanislav Levin <slev@altlinux.org>
2022-08-16 19:35:37 +02:00
Florence Blanc-Renaud 2385d1d90a ipatests: Fix expected object classes
Because the sidgen plugin is a postop plugin, it is not
always triggered before the result of an ADD is returned
and the objectclasses of the user may / may not contain
ipantuserattrs.
Fix the expected object classes.

Related: https://pagure.io/freeipa/issue/9062
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Stanislav Levin <slev@altlinux.org>
2022-08-16 19:35:37 +02:00
Thomas Woerner 5e2e4664ae DNSResolver: Fix use of nameservers with ports
IPA DNS zone and forwardzone commands allow to use nameservers with ports
as "SERVER_IP port PORT_NUMBER". bind is supporting this syntax, but the
Resolver in dnspython that is used to verify the list of forwarders
(nameservers) is only allowing to have IP addresses in this list. With
dnspython version 2.20 there is a new validator in dns.resolver.BaseResolver
that ensures this.

Refs:
- https://bind9.readthedocs.io/en/v9_18_4/reference.html#zone-statement-grammar
- https://github.com/rthalley/dnspython/blob/master/dns/resolver.py#L1094

ipapython/dnsutil.DNSResolver derives from dns.resolver.Resolver. The setter
for nameservers has been overloaded in the DNSResolver class to split out
the port numbers into the nameserver_ports dict { SERVER_IP: PORT_NUMBER }.
After the setter for nameservers succeeded, nameserver_ports is set.
nameserver_ports is used in the resolve() method of dns.resolver.Resolver.

Additional tests have been added to verify that nameservers and also
nameserver_ports are properly set and also valid.

Fixes: https://pagure.io/freeipa/issue/9158

Signed-off-by: Thomas Woerner <twoerner@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
2022-08-16 19:31:18 +02:00
Rob Crittenden aaf57185a2 upgrades: Don't restart the CA on ACME and profile schema change
There are currently three sets of CA schema changes applied
in ipa-server-upgrade:

* addition of ACME schema
* addition of certificate profile schema
* addition of lightweight CA schema

None of these require a restart of the CA to be supported.

There is an issue in schema parsing such that it doesn't handle
X-ORIGIN properly. A difference is detected and a change applied
but no change is recorded in LDAP so every time upgrade is
run it thinks a CA restart is needed. The CA is not quick to
restart so avoiding one is best, particularly when the update is
run as part of an rpm transaction where a user with an itchy finger
may think things have hung and break out of it.

https://github.com/389ds/389-ds-base/issues/5366 was
filed to track this.

Related: https://pagure.io/freeipa/issue/9204

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
2022-08-16 13:15:41 +02:00
Florence Blanc-Renaud 05a298f564 check_repl_update: in progress is a boolean
With the fix for https://pagure.io/freeipa/issue/9171,
nsds5replicaUpdateInProgress is now handled as a boolean.
One remaining occurrence was still handling it as a string
and calling lower() on its value.

Replace with direct boolean comparison.

Fixes: https://pagure.io/freeipa/issue/9218
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
2022-08-16 13:10:10 +02:00
Carla Martinez ade5093b08 webui: Allow grace login limit
There was no support for setting the grace login limit on the WebUI. The
only way to so was only via CLI:

   `ipa pwpolicy-mod --gracelimit=2 global_policy`

Thus, the grace login limit must be updated from the policy section and
this will reflect also on the user settings (under the 'Password Policy'
section)

Fixes: https://pagure.io/freeipa/issue/9211

Signed-off-by: Carla Martinez <carlmart@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
2022-08-09 12:00:00 +02:00
Sudhir Menon a5762621ef ipatests: ipa-client-install --subid adds entry in nsswitch.conf
This testcase checks that when ipa-client-install command
is run with --subid option, /etc/nsswitch.conf file is updated
with the below entry

subid: nss
Related: https://pagure.io/freeipa/issue/9159

Since the newly added testsuite required client
system, hence modified the below yaml files to change the topology
from *master_1repl to *master_1repl_1client in the below files

gating.yaml
nightly_latest.yaml
nightly_previous.yaml
nightly_rawhide.yaml

Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
Reviewed-By: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
2022-08-09 08:35:07 +02:00
Florence Blanc-Renaud d40fd28783 azure tests: disable TestInstallDNSSECFirst
The test TestInstallDNSSECFirst is failing because of one of its
dependencies (the most likely suspect is the update of openssl-pkcs11).
Disable the test from azure gating until the issue is solved.

Related: https://pagure.io/freeipa/issue/9216
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
Reviewed-By: Carla Martinez <carlmart@redhat.com>
2022-08-08 14:35:11 +02:00
David Pascual 6483f33389 ipatest: fix prci checker target masked return code & add pylint
In the yamllint target of makefile, prci_checker result was being masked by echo statement.
Aditionally, prci_checker script has been added to the list of Python sources to be Pylinted.

Addressing comments of recently merged PR:
https://github.com/freeipa/freeipa/pull/6301#discussion_r923163970
https://github.com/freeipa/freeipa/pull/6301#issuecomment-1187037261

Signed-off-by: David Pascual <davherna@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
2022-08-04 13:23:26 -04:00
Rob Crittenden 1316cd8b22 Disabling gracelimit does not prevent LDAP binds
Originally the code treated 0 as disabled. This was
changed during the review process to -1 but one remnant
was missed effetively allowing gracelimit 0 to also mean
disabled.

Add explicit tests for testing with gracelimit = 0 and
gracelimit = -1.

Also remove some extranous "str(self.master.domain.basedn)"
lines from some of the tests.

Fixes: https://pagure.io/freeipa/issue/9206

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
2022-08-01 13:03:51 -04:00
Erik f962a0c283 ipatests: healthcheck: test if system is FIPS enabled
Test if FIPS is enabled and the check exists.

Related: https://pagure.io/freeipa/issue/8951

Signed-off-by: Erik Belko <ebelko@redhat.com>
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-08-01 09:22:37 -04:00
Stanislav Levin 1ada42e3bc ap: Constrain supported docutils
New Sphinx 5.1.0 (Released: Jul 24, 2022) bumped supported docutils
to 0.19:
https://github.com/sphinx-doc/sphinx/pull/10656

But m2r2 doesn't support it yet:
https://github.com/CrossNox/m2r2/issues/52

Thereby, docutils must be constrained to < 0.19.

This should be fixed by m2r2 and after they do it the restriction
can be removed.

Fixes: https://pagure.io/freeipa/issue/9208
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-07-26 16:57:54 -04:00
Stanislav Levin b59baf31bc ap: Rearrange overloaded jobs
With some recent changes the Azure Agent has decreased performance.
For example, `InstallDNSSECFirst_1_to_5` (5 subjobs) job took ~33min
and now it takes ~40min. In the same time there are jobs having only
1 or 2 subjobs and they should be used more.

Fixes: https://pagure.io/freeipa/issue/9207
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-07-26 16:57:54 -04:00
Stanislav Levin 98c6e96e8d ap: Disable azure's security daemon
This daemon run clamav which is resource aggressive.
No point to run Windows virus scanner on Ubuntu in Linux-only
environment.

Fixes: https://pagure.io/freeipa/issue/9207
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-07-26 16:57:54 -04:00
Stanislav Levin e77b0b08d7 ap: Raise dbus timeout
With some recent changes on Azure Agent the default DBus call
timeout is not good enough. For example, in case of
`InstallDNSSECFirst_1_to_5` job hostnamectl received reply in ~20sec,
but later it increased to ~30sec (more subjobs - more time to reply).
It's good to raise this timeout to be more protected against minimum
performance times.

https://www.freedesktop.org/software/systemd/man/sd_bus_set_method_call_timeout.html#Description

Fixes: https://pagure.io/freeipa/issue/9207
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-07-26 16:57:54 -04:00
Rob Crittenden b31631ad69 Warn for permissions with read/write/search/compare and no attrs
An ACI with rights of read, write, search and/or compare without
attributes to apply the rights to is effectively a no-op. Allow
the ACI to be created but include a warning. Ignore the add
and delete rights. While they make no sense in the context of
the other rights we should still warn that they are a no-op
with no attributes.

Use the existing make_aci() object method to create the
message and update the add/mod callers to capture and add the
message to the result if one is provided.

When updating an existing ACI the effective attributes will
not be included so fall back to the attributes in the resulting
permission.

Prior to checking for rights and attributes convert any deprecated
names for older clients into the newer values needed by make_aci

This is exercised by existing xmlrpc permission tests that
create such permissions without attributes.

https://pagure.io/freeipa/issue/9188

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
2022-07-16 07:57:49 +02:00
David Pascual f33266c2ba ipatests: Checker script for prci definitions
This script allows developers to check if prci definition jobs have the correct format,
which is defined in prci_jobs_spec.yaml
Useful when adding new jobs to the definitions.

Signed-off-by: David Pascual <davherna@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Petr Vobornik <pvoborni@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Petr Vobornik <pvoborni@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Stanislav Levin <slev@altlinux.org>
2022-07-16 07:53:57 +02:00
Armando Neto 991849cf58 webui: Do not allow empty pagination size
Pagination size must be required, the current validators are triggered after
form is submitted, thus the only way for check if data is not empty is by making
the field required.

Fixes: https://pagure.io/freeipa/issue/9192

Signed-off-by: Armando Neto <abiagion@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
2022-07-12 13:53:54 -03:00
Fraser Tweedale cbf2614d8a install: suggest --skip-mem-check when mem check fails
In the memory check failure message, add a hint to the administrator
that they can use the --skip-mem-check flag to skip the check.

Related: https://pagure.io/freeipa/issue/8404

Signed-off-by: Fraser Tweedale <frase@frase.id.au>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
2022-07-06 09:48:18 +02:00
Fraser Tweedale 585cebb1a9 man: add --skip-mem-check to man pages
Document the --skip-mem-check flag in the ipa-server-install(1) and
ipa-replica-install(1) man pages.

Related: https://pagure.io/freeipa/issue/8404

Signed-off-by: Fraser Tweedale <frase@frase.id.au>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
2022-07-06 09:48:18 +02:00
Rob Crittenden 3675bd1d7a Only calculate LDAP password grace when the password is expired
The user's pwd expiration was retrieved but inadvertently was never
compared to current time. So any LDAP bind, including from the
IPA API, counted against the grace period. There is no need to go
through the graceperiod code for non-expired passwords.

https://pagure.io/freeipa/issue/1539

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
2022-06-30 14:56:06 -04:00
Rob Crittenden de64d6724e Fix test_secure_ajp_connector.py failing with Python 3.6.8
Some of the test data are not expected to cause a rewrite in
the upgrade code. Those that do will set the rewrite flag.

In that case there is a new server.xml to be read. This is
handled with mock_open(). The contents can be retrieved via
mocked_file().write.call_args but the repr() of it is:

call(b'<Server port="1234" shutdown="SHUTDOWN">\n  ...')

In at least Python 3.10 one can use write.call_args.args to get
just the raw data. This does not work with Python 3.6.8 and
returns the string 'args' instead results in a TypeError.

TypeError: a bytes-like object is required, not 'str'

Instead drop the args and use the data directly.

For the case of x = mocked_file().write.call_args:

   x[0] is a tuple with the first element being the data
   x[0][0] is the raw data

So use x[0][0] to get at the data instead of x.args[0]

Fixes: https://pagure.io/freeipa/issue/9190

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
2022-06-24 11:36:39 +02:00
Matthew Davis 4f15804270 Add missing parameter to Suse modify_nsswitch_pam_stack
Add missing subid parameter for Suse.

Fixes: https://pagure.io/freeipa/issue/9185
Signed-off-by: Matthew Davis <github@virtual.drop.net>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-06-23 17:46:03 -04:00
Anuja More 15f454f6f8 ipatests: Fix install_master for test_idp.py
For install_master added --no-dnssec-validation.

Fixes: https://pagure.io/freeipa/issue/9189

Signed-off-by: Anuja More <amore@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
2022-06-23 17:45:16 -04:00
Timo Aaltonen 56c8270997 ipaplatform/debian: Drop the path for ldap.so
Named is able to find plugins if they are installed in the plugindir,
so drop the hardcoded path from named.conf.

Signed-off-by: Timo Aaltonen <tjaalton@debian.org>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-06-23 17:44:11 -04:00
Timo Aaltonen c39c2ee80d ipaplatform/debian: Use multiarch path for libsofthsm2.so
The library moved there some years ago, and the compat symlink might go
away at some point. Better prepare for it.

Signed-off-by: Timo Aaltonen <tjaalton@debian.org>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-06-23 17:44:11 -04:00
Michal Polovka afa94c7995 ipatests: Healthcheck use subject base from IPA not REALM
Test if healthcheck uses cert subject base from IPA and not from
REALM. This prevents false-positive errors when the subject base is
customized.

Related: https://github.com/freeipa/freeipa-healthcheck/issues/253

Signed-off-by: Michal Polovka <mpolovka@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-06-23 17:43:08 -04:00
Anuja More 50b4d9ab3f ipatests: update prci definitions for test_idp.py
Signed-off-by: Anuja More <amore@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
2022-06-23 13:44:02 +02:00
Anuja More 857713c5a9 Add end to end integration tests for external IdP
Added tests for HBAC and SUDO rule and other
test scenarios.

Related : https://pagure.io/freeipa/issue/8805
Related: https://pagure.io/freeipa/issue/8803
Related: https://pagure.io/freeipa/issue/8804

Signed-off-by: Anuja More <amore@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
2022-06-23 13:44:02 +02:00
Florence Blanc-Renaud 4984ff210a xmlrpc tests: updated expected output for preserved user
Update the expected summary for the command
ipa user-del --preserve

The command now displays: Preserved user: user1
instead of                Deleted user: user1

Related: https://pagure.io/freeipa/issue/9187

Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-06-23 08:39:14 +02:00
Florence Blanc-Renaud ff4152539b Preserve user: fix the confusing summary
When ipa user-del --preserve is called, the command output
prints a summary with:
    Deleted user: user1
although the user was preserved.
Replace the summary with
    Preserved user: user1
to reflect what was actually done.

Fixes: https://pagure.io/freeipa/issue/9187
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-06-23 08:39:14 +02:00
Michal Polovka 206e08d811 ipatests: Healthcheck should ignore pki errors when CA is not configured
Test if ipa-healthcheck complains about pki.server.healthcheck errors
when CA is not configured on the replica.

Related: https://github.com/freeipa/freeipa-healthcheck/issues/201

Signed-off-by: Michal Polovka <mpolovka@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
2022-06-17 16:41:08 +02:00
Michal Polovka a6a6781284 ipatests: Increase expect timeout for interactive mode
Increase the default timeout for expect function when testing
interactive mode to mitigate an issue when the tests are failing
on the slow systems.

Fixes: https://pagure.io/freeipa/issue/9183

Signed-off-by: Michal Polovka <mpolovka@redhat.com>
Reviewed-By: Mohammad Rizwan Yusuf <myusuf@redhat.com>
2022-06-17 16:39:08 +02:00
Antonio Torres 3e90842b3d Back to git snapshots
Signed-off-by: Antonio Torres <antorres@redhat.com>
2022-06-15 16:42:51 +02:00
56 changed files with 1654 additions and 134 deletions

No files matched your search

+1
View File
@@ -155,6 +155,7 @@ install/custodia/ipa-custodia-dmldap
install/custodia/ipa-custodia-pki-tomcat
install/custodia/ipa-custodia-pki-tomcat-wrapped
install/custodia/ipa-custodia-ra-agent
install/oddjob/org.freeipa.server.config-enable-sid
install/oddjob/org.freeipa.server.trust-enable-agent
install/oddjob/com.redhat.idm.trust-fetch-domains
install/oddjob/etc/oddjobd.conf.d/ipa-server.conf
+9 -2
View File
@@ -23,6 +23,8 @@ PYTHON_SCRIPT_SUBDIRS = \
$(top_builddir)/install/tools \
$(NULL)
PRCI_DEFINITIONS_DIR = $(top_srcdir)/ipatests/prci_definitions
AZURE_PYTHON_SCRIPT_SUBDIR = $(top_builddir)/ipatests/azure
IPA_PLACEHOLDERS = freeipa ipa ipaserver ipatests
@@ -336,6 +338,8 @@ endif # WITH_RPMLINT
# There are Jinja yaml templates, which differ from reqular ones. These
# files should be placed on skip list (YAML_TEMPLATE_FILES), otherwise
# safe_load fails.
# Also check PRCI definitions yaml files jobs format and content with
# prci_checker script
.PHONY: yamllint
yamllint:
YAML_TEMPLATE_FILES="\
@@ -357,7 +361,10 @@ yamllint:
echo $${YAML}; \
$(PYTHON) -c "import yaml; f = open('$${YAML}'); yaml.safe_load(f); f.close()" || { echo Your YAML file: $${YAML} has a wrong syntax or this is a Jinja template. In the latter clause, consider to add your YAML file to the YAML_TEMPLATE_FILES list in Makefile.am.; exit 1; } \
done; \
echo "-----------"
echo -e "\nCheck PRCI definitions";
@echo "-----------"
$(PYTHON) $(PRCI_DEFINITIONS_DIR)/prci_checker.py -d $(PRCI_DEFINITIONS_DIR) -s $(PRCI_DEFINITIONS_DIR)/prci_jobs_spec.yaml;
@echo "-----------"
# Build & lint documentation.
#
@@ -389,7 +396,7 @@ pylint: $(GENERATED_PYTHON_FILES) ipasetup.py python_scripts
-name '*~' -o \
-name '*.py' -print -o \
-type f -exec grep -qsm1 '^#!.*\bpython' '{}' \; -print`; \
FILES=`echo -e "$${FILES}\n$(AZURE_PYTHON_SCRIPT_SUBDIR)"`; \
FILES=`echo -e "$${FILES}\n$(AZURE_PYTHON_SCRIPT_SUBDIR)\n$(PRCI_DEFINITIONS_DIR)"`; \
echo -e "Pylint on $(PYTHON) is running over files:\n$${FILES}\nPlease wait ...\n"; \
$(PYTHON) -m pylint --version; \
PYTHONPATH=$(top_srcdir) $(PYTHON) -m pylint \
+2 -2
View File
@@ -21,7 +21,7 @@
########################################################
define(IPA_VERSION_MAJOR, 4)
define(IPA_VERSION_MINOR, 9)
define(IPA_VERSION_RELEASE, 10)
define(IPA_VERSION_RELEASE, 11)
########################################################
# For 'pre' releases the version will be #
@@ -49,7 +49,7 @@ define(IPA_VERSION_PRE_RELEASE,)
# This option works only with GNU m4: #
# it requires esyscmd m4 macro. #
########################################################
define(IPA_VERSION_IS_GIT_SNAPSHOT, no)
define(IPA_VERSION_IS_GIT_SNAPSHOT, yes)
########################################################
# git development branch: #
@@ -359,7 +359,8 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb)
Slapi_ValueSet *values = NULL;
long grace_limit = 0;
int grace_user_time;
char *pwd_expiration = NULL;
char *tmpstr = NULL;
time_t pwd_expiration;
int pwresponse_requested = 0;
Slapi_PBlock *pbtm = NULL;
Slapi_Mods *smods = NULL;
@@ -414,12 +415,17 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb)
}
slapi_value_free(&objectclass);
pwd_expiration = slapi_entry_attr_get_charptr(target_entry, "krbPasswordExpiration");
if (pwd_expiration == NULL) {
tmpstr = slapi_entry_attr_get_charptr(target_entry, "krbPasswordExpiration");
if (tmpstr == NULL) {
/* No expiration means nothing to do */
LOG_TRACE("No krbPasswordExpiration for %s, nothing to do\n", dn);
goto done;
}
pwd_expiration = ipapwd_gentime_to_time_t(tmpstr);
if (pwd_expiration > time(NULL)) {
/* Not expired, nothing to see here */
goto done;
}
ldrc = ipagraceperiod_getpolicy(target_entry, &policy_entry,
&values, &actual_type_name,
@@ -473,7 +479,7 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb)
if (pwresponse_requested) {
slapi_pwpolicy_make_response_control(pb, -1, grace_limit - grace_user_time , -1);
}
} else if ((grace_limit > 0) && (grace_user_time >= grace_limit)) {
} else if (grace_user_time >= grace_limit) {
LOG_TRACE("%s password is expired and out of grace limit\n", dn);
errstr = "Password is expired.\n";
ret = LDAP_INVALID_CREDENTIALS;
+2 -1
View File
@@ -35,4 +35,5 @@ distclean: clean
venv:
$(PYTHON) -m venv $(VENVDIR)
$(VENVDIR)/bin/python3 -m pip install -U pip setuptools
$(VENVDIR)/bin/python3 -m pip install -U -r ./requirements.txt
$(VENVDIR)/bin/python3 -m pip install -U -r ./requirements.txt \
-c constraints.txt
+1
View File
@@ -0,0 +1 @@
docutils < 0.19
+1
View File
@@ -98,6 +98,7 @@ dist_app_DATA = \
kdcproxy.wsgi \
ipakrb5.aug \
ipa.conf.template \
ipa-wsgi.conf.template \
ipa-kdc-proxy.conf.template \
ipa-pki-proxy.conf.template \
ipa-rewrite.conf.template \
+53
View File
@@ -0,0 +1,53 @@
#
# VERSION 32 - DO NOT REMOVE THIS LINE
#
# This file may be overwritten on upgrades.
#
# FIXME: WSGISocketPrefix is a server-scope directive. The mod_wsgi package
# should really be fixed by adding this its /etc/httpd/conf.d/wsgi.conf:
WSGISocketPrefix $WSGI_PREFIX_DIR
# Configure mod_wsgi handler for /ipa
WSGIDaemonProcess ipa processes=$WSGI_PROCESSES threads=1 maximum-requests=500 \
user=ipaapi group=ipaapi display-name=%{GROUP} socket-timeout=2147483647 \
lang=C.UTF-8 locale=C.UTF-8
WSGIImportScript /usr/share/ipa/wsgi.py process-group=ipa application-group=ipa
<VirtualHost *:80>
ServerName $FQDN
ServerAlias ipa-ca.$DOMAIN
# Logging
CustomLog /var/log/httpd/ipa-access.log combined
ErrorLog /var/log/httpd/ipa-error.log
IncludeOptional $HTTPD_IPA_PKI_PROXY_CONF
Include $HTTPD_IPA_REWRITE_CONF
Include $HTTPD_IPA_CONF
</VirtualHost>
# the below section is only need if you have above 3 lines and want SSL
<VirtualHost *:443>
ServerName $FQDN
ServerAlias ipa-ca.$DOMAIN
# Logging
CustomLog /var/log/httpd/ipa-access.log combined
ErrorLog /var/log/httpd/ipa-error.log
# SSL
SSLEngine on
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCACertificateFile /etc/ipa/ca.crt
SSLCertificateKeyFile /var/lib/ipa/private/httpd.key
SSLCertificateFile /var/lib/ipa/certs/httpd.crt
SSLCipherSuite PROFILE=SYSTEM
SSLProxyCipherSuite PROFILE=SYSTEM
#
IncludeOptional $HTTPD_IPA_KDCPROXY_CONF_SYMLINK
IncludeOptional $HTTPD_IPA_PKI_PROXY_CONF
Include $HTTPD_IPA_REWRITE_CONF
Include $HTTPD_IPA_CONF
</VirtualHost>
+15
View File
@@ -0,0 +1,15 @@
#
# VERSION 32 - DO NOT REMOVE THIS LINE
#
# This file may be overwritten on upgrades.
#
# FIXME: WSGISocketPrefix is a server-scope directive. The mod_wsgi package
# should really be fixed by adding this its /etc/httpd/conf.d/wsgi.conf:
WSGISocketPrefix $WSGI_PREFIX_DIR
# Configure mod_wsgi handler for /ipa
WSGIDaemonProcess ipa processes=$WSGI_PROCESSES threads=1 maximum-requests=500 \
user=ipaapi group=ipaapi display-name=%{GROUP} socket-timeout=2147483647 \
lang=C.UTF-8 locale=C.UTF-8
-7
View File
@@ -35,14 +35,7 @@ AddOutputFilterByType DEFLATE text/html text/plain text/xml \
application/javascript application/json text/css \
application/x-font-ttf
# FIXME: WSGISocketPrefix is a server-scope directive. The mod_wsgi package
# should really be fixed by adding this its /etc/httpd/conf.d/wsgi.conf:
WSGISocketPrefix $WSGI_PREFIX_DIR
# Configure mod_wsgi handler for /ipa
WSGIDaemonProcess ipa processes=$WSGI_PROCESSES threads=1 maximum-requests=500 \
user=ipaapi group=ipaapi display-name=%{GROUP} socket-timeout=2147483647 \
lang=C.UTF-8 locale=C.UTF-8
WSGIScriptAlias /ipa /usr/share/ipa/wsgi.py process-group=ipa \
application-group=%{GLOBAL}
WSGIScriptReloading Off
+3
View File
@@ -98,6 +98,9 @@ Configure SSSD as data source for subid.
\fB\-\-skip\-conncheck\fR
Skip connection check to remote master
.TP
\fB\-\-skip\-mem\-check\fR
Skip checking for minimum required memory
.TP
\fB\-d\fR, \fB\-\-debug
Enable debug logging when more verbose output is needed
.TP
+3
View File
@@ -83,6 +83,9 @@ Do not configure OpenSSH server.
\fB\-\-subid\fR
Configure SSSD as data source for subid.
.TP
\fB\-\-skip\-mem\-check\fR
Skip checking for minimum required memory
.TP
\fB\-d\fR, \fB\-\-debug\fR
Enable debug logging when more verbose output is needed.
.TP
@@ -318,6 +318,7 @@ define([
$type: 'text',
name: 'pagination_size',
label: '@i18n:customization.table_pagination',
required: true,
validators: ['positive_integer']
}
]
+3
View File
@@ -72,6 +72,9 @@ return {
{
name: 'cospriority',
required: true
},
{
name: 'passwordgracelimit'
}
]
}]
+5
View File
@@ -318,6 +318,11 @@ return {
label: '@mo-param:pwpolicy:krbpwdlockoutduration:label',
read_only: true,
measurement_unit: 'seconds'
},
{
name: 'passwordgracelimit',
label: '@mo-param:pwpolicy:passwordgracelimit:label',
read_only: true
}
]
},
+12 -2
View File
@@ -18,7 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Custom message (debug, info, wraning) classes passed through RPC.
Custom message (debug, info, warning) classes passed through RPC.
These are added to the "messages" entry in a RPC response, and printed to the
user as log messages.
@@ -27,7 +27,7 @@ Each message class has a unique numeric "errno" attribute from the 10000-10999
range, so that it does not clash with PublicError numbers.
Messages also have the 'type' argument, set to one of 'debug', 'info',
'warning', 'error'. This determines the severity of themessage.
'warning', 'error'. This determines the severity of the message.
"""
from __future__ import print_function
@@ -496,6 +496,16 @@ class LightweightCACertificateNotAvailable(PublicMessage):
format = _("The certificate for %(ca)s is not available on this server.")
class MissingTargetAttributesinPermission(PublicMessage):
"""
**13032** A permission was added with no target attributes
"""
errno = 13032
type = "warning"
format = _("The permission has %(right)s rights but no attributes "
"are set.")
def iter_messages(variables, base):
"""Return a tuple with all subclasses
"""
+7 -4
View File
@@ -45,11 +45,14 @@ class BasePathNamespace:
GSSAPI_SESSION_KEY = "/etc/httpd/alias/ipasession.key"
OLD_KRA_AGENT_PEM = "/etc/httpd/alias/kra-agent.pem"
HTTPD_CONF_D_DIR = "/etc/httpd/conf.d/"
HTTPD_IPA_DIR = "/etc/httpd/ipa"
HTTPD_IPA_KDCPROXY_CONF = "/etc/ipa/kdcproxy/ipa-kdc-proxy.conf"
HTTPD_IPA_KDCPROXY_CONF_SYMLINK = "/etc/httpd/conf.d/ipa-kdc-proxy.conf"
HTTPD_IPA_PKI_PROXY_CONF = "/etc/httpd/conf.d/ipa-pki-proxy.conf"
HTTPD_IPA_REWRITE_CONF = "/etc/httpd/conf.d/ipa-rewrite.conf"
HTTPD_IPA_CONF = "/etc/httpd/conf.d/ipa.conf"
HTTPD_IPA_KDCPROXY_CONF_SYMLINK = "/etc/httpd/ipa/ipa-kdc-proxy.conf"
HTTPD_IPA_PKI_PROXY_CONF = "/etc/httpd/ipa/ipa-pki-proxy.conf"
HTTPD_IPA_REWRITE_CONF = "/etc/httpd/ipa/ipa-rewrite.conf"
HTTPD_IPA_CONF = "/etc/httpd/ipa/ipa.conf"
HTTPD_IPA_SITE_CONF = "/etc/httpd/conf.d/vhost-ipa.conf"
HTTPD_IPA_WSGI_CONF = "/etc/httpd/ipa/ipa-wsgi.conf"
HTTPD_NSS_CONF = "/etc/httpd/conf.d/nss.conf"
HTTPD_SSL_CONF = "/etc/httpd/conf.d/ssl.conf"
HTTPD_SSL_SITE_CONF = "/etc/httpd/conf.d/ssl.conf"
+9 -5
View File
@@ -20,10 +20,13 @@ class DebianPathNamespace(BasePathNamespace):
ETC_HTTPD_DIR = "/etc/apache2"
HTTPD_ALIAS_DIR = "/etc/apache2/ipa"
HTTPD_CONF_D_DIR = "/etc/apache2/conf-enabled/"
HTTPD_IPA_KDCPROXY_CONF_SYMLINK = "/etc/apache2/conf-enabled/ipa-kdc-proxy.conf"
HTTPD_IPA_PKI_PROXY_CONF = "/etc/apache2/conf-enabled/ipa-pki-proxy.conf"
HTTPD_IPA_REWRITE_CONF = "/etc/apache2/conf-available/ipa-rewrite.conf"
HTTPD_IPA_CONF = "/etc/apache2/conf-enabled/ipa.conf"
HTTPD_IPA_DIR = "/etc/apache2/ipa/"
HTTPD_IPA_KDCPROXY_CONF_SYMLINK = "/etc/apache2/ipa/ipa-kdc-proxy.conf"
HTTPD_IPA_PKI_PROXY_CONF = "/etc/apache2/ipa/ipa-pki-proxy.conf"
HTTPD_IPA_REWRITE_CONF = "/etc/apache2/ipa/ipa-rewrite.conf"
HTTPD_IPA_CONF = "/etc/apache2/ipa/ipa.conf"
HTTPD_IPA_WSGI_CONF = "/etc/apache2/ipa/ipa-wsgi.conf"
HTTPD_IPA_SITE_CONF = "/etc/apache2/sites-available/ipa.conf"
HTTPD_NSS_CONF = "/etc/apache2/mods-available/nss.conf"
HTTPD_SSL_CONF = "/etc/apache2/mods-available/ssl.conf"
HTTPD_SSL_SITE_CONF = "/etc/apache2/sites-available/default-ssl.conf"
@@ -79,8 +82,9 @@ class DebianPathNamespace(BasePathNamespace):
UPDATE_CA_TRUST = "/usr/sbin/update-ca-certificates"
BIND_LDAP_DNS_IPA_WORKDIR = "/var/cache/bind/dyndb-ldap/ipa/"
BIND_LDAP_DNS_ZONE_WORKDIR = "/var/cache/bind/dyndb-ldap/ipa/master/"
BIND_LDAP_SO = "ldap.so"
LIBARCH = "/{0}".format(MULTIARCH)
LIBSOFTHSM2_SO = "/usr/lib/softhsm/libsofthsm2.so"
LIBSOFTHSM2_SO = "/usr/lib/{0}/softhsm/libsofthsm2.so".format(MULTIARCH)
PAM_KRB5_SO = "/usr/lib/{0}/security/pam_krb5.so".format(MULTIARCH)
LIB_SYSTEMD_SYSTEMD_DIR = "/lib/systemd/system/"
LIBEXEC_CERTMONGER_DIR = "/usr/lib/certmonger"
+7 -4
View File
@@ -17,10 +17,13 @@ class SusePathNamespace(BasePathNamespace):
HTTPD_ALIAS_DIR = "/etc/apache2/ipa"
GSSAPI_SESSION_KEY = "/etc/apache2/ipa/ipasession.key"
HTTPD_CONF_D_DIR = "/etc/apache2/conf.d/"
HTTPD_IPA_KDCPROXY_CONF_SYMLINK = "/etc/apache2/conf.d/ipa-kdc-proxy.conf"
HTTPD_IPA_PKI_PROXY_CONF = "/etc/apache2/conf.d/ipa-pki-proxy.conf"
HTTPD_IPA_REWRITE_CONF = "/etc/apache2/conf.d/ipa-rewrite.conf"
HTTPD_IPA_CONF = "/etc/apache2/conf.d/ipa.conf"
HTTPD_IPA_DIR = "/etc/apache2/ipa/"
HTTPD_IPA_KDCPROXY_CONF_SYMLINK = "/etc/apache2/ipa/ipa-kdc-proxy.conf"
HTTPD_IPA_PKI_PROXY_CONF = "/etc/apache2/ipa/ipa-pki-proxy.conf"
HTTPD_IPA_REWRITE_CONF = "/etc/apache2/ipa/ipa-rewrite.conf"
HTTPD_IPA_CONF = "/etc/apache2/ipa/ipa.conf"
HTTPD_IPA_WSGI_CONF = "/etc/apache2/ipa/ipa-wsgi.conf"
HTTPD_IPA_SITE_CONF = "/etc/apache2/conf.d/ipa-vhost.conf"
HTTPD_NSS_CONF = "/etc/apache2/conf.d/nss.conf"
HTTPD_SSL_CONF = "/etc/apache2/conf.d/ssl.conf"
HTTPD_SSL_SITE_CONF = "/etc/apache2/conf.d/ssl.conf"
+1 -1
View File
@@ -45,7 +45,7 @@ class SuseTaskNamespace(RedHatTaskNamespace):
return False # FIXME: Implement after libexec move
def modify_nsswitch_pam_stack(self, sssd, mkhomedir, statestore,
sudo=True):
sudo=True, subid=False):
# pylint: disable=ipa-forbidden-import
from ipalib import sysrestore # FixMe: break import cycle
# pylint: enable=ipa-forbidden-import
+41
View File
@@ -144,6 +144,47 @@ class DNSResolver(dns.resolver.Resolver):
nameservers.remove(ipv4_loopback)
self.nameservers = nameservers
@dns.resolver.Resolver.nameservers.setter
def nameservers(self, nameservers):
"""
*nameservers*, a ``list`` of nameservers with optional ports:
"SERVER_IP port PORT_NUMBER".
Overloads dns.resolver.Resolver.nameservers setter to split off ports
into nameserver_ports after setting nameservers successfully with the
setter in dns.resolver.Resolver.
"""
# Get nameserver_ports if it is already set
if hasattr(self, "nameserver_ports"):
nameserver_ports = self.nameserver_ports
else:
nameserver_ports = {}
# Check nameserver items in list and split out converted port number
# into nameserver_ports: { nameserver: port }
if isinstance(nameservers, list):
_nameservers = []
for nameserver in nameservers:
splits = nameserver.split()
if len(splits) == 3 and splits[1] == "port":
nameserver = splits[0]
try:
port = int(splits[2])
if port < 0 or port > 65535:
raise ValueError()
except ValueError:
raise ValueError(
"invalid nameserver: %s is not a valid port" %
splits[2])
nameserver_ports[nameserver] = port
_nameservers.append(nameserver)
nameservers = _nameservers
# Call dns.resolver.Resolver.nameservers setter
dns.resolver.Resolver.nameservers.__set__(self, nameservers)
# Set nameserver_ports after successfull call to setter
self.nameserver_ports = nameserver_ports
class DNSZoneAlreadyExists(dns.exception.DNSException):
supp_kwargs = {'zone', 'ns'}
+42 -4
View File
@@ -87,13 +87,14 @@ class HTTPInstance(service.Service):
def create_instance(self, realm, fqdn, domain_name, dm_password=None,
pkcs12_info=None,
subject_base=None, auto_redirect=True, ca_file=None,
ca_is_configured=None, promote=False,
subject_base=None, auto_redirect=True, ui_vhost=False,
ca_file=None, ca_is_configured=None, promote=False,
master_fqdn=None):
self.fqdn = fqdn
self.realm = realm
self.domain = domain_name
self.dm_password = dm_password
self.ui_vhost = ui_vhost
self.suffix = ipautil.realm_to_suffix(self.realm)
self.pkcs12_info = pkcs12_info
self.cert = None
@@ -112,6 +113,10 @@ class HTTPInstance(service.Service):
IPA_CCACHES=paths.IPA_CCACHES,
WSGI_PREFIX_DIR=paths.WSGI_PREFIX_DIR,
WSGI_PROCESSES=constants.WSGI_PROCESSES,
HTTPD_IPA_KDCPROXY_CONF_SYMLINK=paths.HTTPD_IPA_KDCPROXY_CONF_SYMLINK,
HTTPD_IPA_PKI_PROXY_CONF=paths.HTTPD_IPA_PKI_PROXY_CONF,
HTTPD_IPA_REWRITE_CONF=paths.HTTPD_IPA_REWRITE_CONF,
HTTPD_IPA_CONF=paths.HTTPD_IPA_CONF
)
self.ca_file = ca_file
if ca_is_configured is not None:
@@ -198,6 +203,14 @@ class HTTPInstance(service.Service):
# https://pagure.io/freeipa/issue/7662
tasks.restore_context(session_dir)
# create /etc/httpd/ipa
if not os.path.isdir(paths.HTTPD_IPA_DIR):
os.makedirs(paths.HTTPD_IPA_DIR)
# Must be world-readable / executable
os.chmod(paths.HTTPD_IPA_DIR, 0o755)
# Restore SELinux context of /etc/httpd/ipa
tasks.restore_context(paths.HTTPD_IPA_DIR)
target_fname = paths.HTTPD_IPA_CONF
http_txt = ipautil.template_file(
os.path.join(paths.USR_SHARE_IPA_DIR,
@@ -209,6 +222,29 @@ class HTTPInstance(service.Service):
http_fd.close()
os.chmod(target_fname, 0o644)
target_fname = paths.HTTPD_IPA_WSGI_CONF
http_txt = ipautil.template_file(
os.path.join(paths.USR_SHARE_IPA_DIR,
"ipa-wsgi.conf.template"),
self.sub_dict)
self.fstore.backup_file(paths.HTTPD_IPA_CONF)
http_fd = open(target_fname, "w")
http_fd.write(http_txt)
http_fd.close()
os.chmod(target_fname, 0o644)
target_fname = paths.HTTPD_IPA_SITE_CONF
http_txt = ipautil.template_file(
os.path.join(paths.USR_SHARE_IPA_DIR,
"ipa-vhost.conf.template"),
self.sub_dict)
self.fstore.backup_file(paths.HTTPD_IPA_SITE_CONF)
http_fd = open(target_fname, "w")
http_fd.write(http_txt)
http_fd.close()
os.chmod(target_fname, 0o644)
target_fname = paths.HTTPD_IPA_REWRITE_CONF
http_txt = ipautil.template_file(
os.path.join(paths.USR_SHARE_IPA_DIR,
@@ -539,8 +575,9 @@ class HTTPInstance(service.Service):
ca_iface.Set('org.fedorahosted.certmonger.ca',
'external-helper', helper)
for f in [paths.HTTPD_IPA_CONF, paths.HTTPD_SSL_CONF,
paths.HTTPD_SSL_SITE_CONF, paths.HTTPD_NSS_CONF]:
for f in [paths.HTTPD_IPA_CONF, paths.HTTPD_IPA_WSGI_CONF,
paths.HTTPD_SSL_CONF, paths.HTTPD_SSL_SITE_CONF,
paths.HTTPD_NSS_CONF]:
try:
self.fstore.restore_file(f)
except ValueError as error:
@@ -554,6 +591,7 @@ class HTTPInstance(service.Service):
paths.HTTPD_PASSWD_FILE_FMT.format(host=api.env.host),
paths.HTTPD_IPA_REWRITE_CONF,
paths.HTTPD_IPA_CONF,
paths.HTTPD_IPA_WSGI_CONF,
paths.HTTPD_IPA_PKI_PROXY_CONF,
paths.HTTPD_IPA_KDCPROXY_CONF_SYMLINK,
paths.HTTPD_IPA_KDCPROXY_CONF,
+2 -1
View File
@@ -1132,7 +1132,8 @@ def check_available_memory(ca=False):
if available < minimum_suggested:
raise ScriptError(
"Less than the minimum 1.2GB of RAM is available, "
"%.2fGB available" % (available / (1024 * 1024 * 1024))
"%.2fGB available. Use --skip-mem-check to suppress this check."
% (available / (1024 * 1024 * 1024))
)
def load_external_cert(files, ca_subject):
+2
View File
@@ -154,6 +154,8 @@ class Backup(admintool.AdminTool):
paths.HTTPD_CERT_FILE,
paths.HTTPD_KEY_FILE,
paths.HTTPD_IPA_CONF,
paths.HTTPD_IPA_SITE_CONF,
paths.HTTPD_WSGI_CONF,
paths.SSHD_CONFIG,
paths.SSHD_IPA_CONFIG,
paths.SSH_CONFIG,
+1 -1
View File
@@ -1152,7 +1152,7 @@ class ReplicationManager:
except (ValueError, TypeError, KeyError):
end = 0
# incremental update is done if inprogress is false and end >= start
done = inprogress and inprogress.lower() == 'false' and start <= end
done = inprogress is not None and not inprogress and start <= end
logger.info("Replication Update in progress: %s: status: %s: "
"start: %d: end: %d",
inprogress, status, start, end)
+6
View File
@@ -317,6 +317,12 @@ class ServerInstallInterface(ServerCertificateInstallInterface,
)
no_ui_redirect = enroll_only(no_ui_redirect)
ui_vhost = knob(
None,
description="Use virtual host for Web UI",
)
ui_vhost = enroll_only(ui_vhost)
dirsrv_config_file = knob(
str, None,
description="The path to LDIF file that will be used to modify "
+2
View File
@@ -943,12 +943,14 @@ def install(installer):
realm_name, host_name, domain_name, dm_password,
pkcs12_info=http_pkcs12_info, subject_base=options.subject_base,
auto_redirect=not options.no_ui_redirect,
ui_vhost=options.ui_vhost,
ca_is_configured=setup_ca)
else:
http.create_instance(
realm_name, host_name, domain_name, dm_password,
subject_base=options.subject_base,
auto_redirect=not options.no_ui_redirect,
ui_vhost=options.ui_vhost,
ca_is_configured=setup_ca)
ca.set_subject_base_in_config(options.subject_base)
+3 -2
View File
@@ -150,7 +150,7 @@ def install_ca_cert(ldap, base_dn, realm, cafile, destfile=paths.IPA_CA_CRT):
return destfile
def install_http(config, auto_redirect, ca_is_configured, ca_file,
def install_http(config, auto_redirect, ui_vhost, ca_is_configured, ca_file,
pkcs12_info=None, fstore=None):
# if we have a pkcs12 file, create the cert db from
# that. Otherwise the ds setup will create the CA
@@ -163,7 +163,7 @@ def install_http(config, auto_redirect, ca_is_configured, ca_file,
http.create_instance(
config.realm_name, config.host_name, config.domain_name,
config.dirman_password, pkcs12_info,
auto_redirect=auto_redirect, ca_file=ca_file,
auto_redirect=auto_redirect, ui_vhost=ui_vhost, ca_file=ca_file,
ca_is_configured=ca_is_configured, promote=True,
subject_base=config.subject_base, master_fqdn=config.master_host_name)
@@ -1315,6 +1315,7 @@ def install(installer):
install_http(
config,
auto_redirect=not options.no_ui_redirect,
ui_vhost=options.ui_vhost,
pkcs12_info=http_pkcs12_info,
ca_is_configured=ca_enabled,
ca_file=cafile,
+29 -1
View File
@@ -152,6 +152,19 @@ def find_autoredirect(fqdn):
return False
return True
def find_uivhost(fqdn):
"""
When upgrading we need to see if the UI virtual host usage
was disabled during install time (or afterward). So sift through the
configuration file and see if we can determine the status.
Returns True if use_vhost is enabled, False otherwise
"""
filename = paths.HTTPD_IPA_SITE_CONF
if os.path.exists(filename):
return True
return False
def find_version(filename):
"""Find the version of a configuration file
@@ -1677,10 +1690,12 @@ def upgrade_configuration():
fix_permissions()
auto_redirect = find_autoredirect(fqdn)
use_vhost = find_usevhost(fqdn)
sub_dict = dict(
REALM=api.env.realm,
FQDN=fqdn,
AUTOREDIR='' if auto_redirect else '#',
UI_VHOST='' if use_vhost else '#',
CRL_PUBLISH_PATH=paths.PKI_CA_PUBLISH_DIR,
DOGTAG_PORT=8009,
CLONE='#',
@@ -1694,6 +1709,10 @@ def upgrade_configuration():
IPA_CUSTODIA_SOCKET=paths.IPA_CUSTODIA_SOCKET,
KDCPROXY_CONFIG=paths.KDCPROXY_CONFIG,
DOMAIN=api.env.domain,
HTTPD_IPA_KDCPROXY_CONF_SYMLINK=paths.HTTPD_IPA_KDCPROXY_CONF_SYMLINK,
HTTPD_IPA_PKI_PROXY_CONF=paths.HTTPD_IPA_PKI_PROXY_CONF,
HTTPD_IPA_REWRITE_CONF=paths.HTTPD_IPA_REWRITE_CONF,
HTTPD_IPA_CONF=paths.HTTPD_IPA_CONF
)
subject_base = find_subject_base()
@@ -1730,6 +1749,12 @@ def upgrade_configuration():
upgrade_file(sub_dict, paths.HTTPD_IPA_CONF,
os.path.join(paths.USR_SHARE_IPA_DIR,
"ipa.conf.template"))
upgrade_file(sub_dict, paths.HTTPD_IPA_SITE_CONF,
os.path.join(paths.USR_SHARE_IPA_DIR,
"ipa-vhost.conf.template"))
upgrade_file(sub_dict, paths.HTTPD_IPA_WSGI_CONF,
os.path.join(paths.USR_SHARE_IPA_DIR,
"ipa-wsgi.conf.template"))
upgrade_file(sub_dict, paths.HTTPD_IPA_REWRITE_CONF,
os.path.join(paths.USR_SHARE_IPA_DIR,
"ipa-rewrite.conf.template"))
@@ -1885,9 +1910,12 @@ def upgrade_configuration():
custodia = custodiainstance.CustodiaInstance(api.env.host, api.env.realm)
custodia.upgrade_instance()
# Don't include schema upgrades in restart consideration, see
# https://pagure.io/freeipa/issue/9204
ca_upgrade_schema(ca)
ca_restart = any([
ca_restart,
ca_upgrade_schema(ca),
certificate_renewal_update(ca, kra, ds, http),
ca_enable_pkix(ca),
ca_configure_profiles_acl(ca),
+43 -6
View File
@@ -28,6 +28,7 @@ from .privilege import validate_permission_to_privilege
from ipalib import errors
from ipalib.parameters import Str, StrEnum, DNParam, Flag
from ipalib import api, _, ngettext
from ipalib import messages
from ipalib.plugable import Registry
from ipalib.capabilities import client_has_capability
from ipalib.aci import ACI
@@ -553,6 +554,7 @@ class permission(baseldap.LDAPObject):
def make_aci(self, entry):
"""Make an ACI string from the given permission entry"""
msg = None
aci_parts = []
name = entry.single_value['cn']
@@ -560,6 +562,18 @@ class permission(baseldap.LDAPObject):
attrs = self.get_effective_attrs(entry)
if attrs:
aci_parts.append("(targetattr = \"%s\")" % ' || '.join(attrs))
else:
# If we are re-parsing an entry for validation then
# the effective attrs may be empty.
if entry.get('attrs') is None:
rights = set(['read', 'write', 'search', 'compare'])
permrights = set(entry['ipapermright'])
for right in ('add', 'delete'):
permrights.discard(right)
if permrights and permrights.issubset(rights):
msg = messages.MissingTargetAttributesinPermission(
right=', '.join(sorted(entry['ipapermright']))
)
# target
ipapermtarget = entry.single_value.get('ipapermtarget')
@@ -608,12 +622,12 @@ class permission(baseldap.LDAPObject):
aci_parts.append('(version 3.0;acl "permission:%s";allow (%s) %s;)' % (
name, ','.join(sorted(entry['ipapermright'])), bindrule))
return ''.join(aci_parts)
return ''.join(aci_parts), msg
def add_aci(self, permission_entry):
"""Add the ACI coresponding to the given permission entry"""
ldap = self.api.Backend.ldap2
acistring = self.make_aci(permission_entry)
acistring, _msg = self.make_aci(permission_entry)
location = permission_entry.single_value.get('ipapermlocation',
self.api.env.basedn)
@@ -641,7 +655,7 @@ class permission(baseldap.LDAPObject):
- entry
- removed ACI string, or None if none existed previously
"""
new_acistring = self.make_aci(permission_entry)
new_acistring, _msg = self.make_aci(permission_entry)
return self._replace_aci(permission_entry, old_name, new_acistring)
def _replace_aci(self, permission_entry, old_name=None, new_acistring=None):
@@ -672,6 +686,25 @@ class permission(baseldap.LDAPObject):
logger.debug('No changes to ACI')
return acientry, acistring
def check_attrs(self, result, *keys, **options):
"""Re-build the ACI to determine if there are rights that
only work when there are attributes defined."""
ldap = self.backend
dn = self.get_dn(*keys, **options)
entry = ldap.make_entry(dn, result['result'])
for old_name, new_name in _DEPRECATED_OPTION_ALIASES.items():
if old_name in entry:
entry[new_name] = entry[old_name]
del entry[old_name]
_acistring, msg = self.make_aci(entry)
if msg:
messages.add_message(
options['version'],
result,
msg,
)
def _get_aci_entry_and_string(self, permission_entry, name=None,
notfound_ok=False, cached_acientry=None):
"""Get the entry and ACI corresponding to the permission entry
@@ -776,7 +809,7 @@ class permission(baseldap.LDAPObject):
target_entry['ipapermlocation'] = [self.api.env.basedn]
# Make sure we're not losing *any info* by the upgrade
new_acistring = self.make_aci(target_entry)
new_acistring, _msg = self.make_aci(target_entry)
if not ACI(new_acistring).isequal(aci):
raise ValueError('Cannot convert ACI, %r != %r' % (new_acistring,
acistring))
@@ -992,7 +1025,9 @@ class permission_add(baseldap.LDAPCreate):
# the whole command, not just the callbacks
def execute(self, *keys, **options):
self.obj.preprocess_options(options, merge_targetfilter=True)
return super(permission_add, self).execute(*keys, **options)
result = super(permission_add, self).execute(*keys, **options)
self.obj.check_attrs(result, *keys, **options)
return result
def get_args(self):
for arg in super(permission_add, self).get_args():
@@ -1094,7 +1129,9 @@ class permission_mod(baseldap.LDAPUpdate):
def execute(self, *keys, **options):
context.filter_ops = self.obj.preprocess_options(
options, return_filter_ops=True)
return super(permission_mod, self).execute(*keys, **options)
result = super(permission_mod, self).execute(*keys, **options)
self.obj.check_attrs(result, *keys, **options)
return result
def get_options(self):
for opt in super(permission_mod, self).get_options():
+4
View File
@@ -56,6 +56,7 @@ from .idviews import remove_ipaobject_overrides
from ipalib.plugable import Registry
from .baseldap import (
LDAPObject,
pkey_to_unicode,
pkey_to_value,
LDAPCreate,
LDAPSearch,
@@ -701,6 +702,7 @@ class user_del(baseuser_del):
__doc__ = _('Delete a user.')
msg_summary = _('Deleted user "%(value)s"')
msg_summary_preserved = _('Preserved user "%(value)s"')
takes_options = baseuser_del.takes_options + (
Bool('preserve?',
@@ -831,6 +833,8 @@ class user_del(baseuser_del):
failed.append(pkey_to_value(pkey, options))
val = dict(result=dict(failed=failed), value=preserved)
val['summary'] = self.msg_summary_preserved % dict(
value=pkey_to_unicode(preserved))
return val
else:
return super(user_del, self).execute(*keys, **options)
@@ -45,15 +45,16 @@ vms:
- test_integration/test_external_ca.py::TestExternalCAInstall
- vm_jobs:
- container_job: InstallDNSSECFirst
containers:
replicas: 1
resources:
replica:
mem_limit: "2400m"
memswap_limit: "3500m"
tests:
- test_integration/test_dnssec.py::TestInstallDNSSECFirst
# Temporary disable - see https://pagure.io/freeipa/issue/9216
# - container_job: InstallDNSSECFirst
# containers:
# replicas: 1
# resources:
# replica:
# mem_limit: "2400m"
# memswap_limit: "3500m"
# tests:
# - test_integration/test_dnssec.py::TestInstallDNSSECFirst
- container_job: simple_replication
containers:
@@ -69,14 +70,6 @@ vms:
tests:
- test_integration/test_service_permissions.py
- container_job: netgroup
tests:
- test_integration/test_netgroup.py
- container_job: external_ca_ExternalCAProfileScenarios
tests:
- test_integration/test_external_ca.py::TestExternalCAProfileScenarios
- vm_jobs:
- container_job: sudo
containers:
@@ -137,6 +130,15 @@ vms:
tests:
- test_integration/test_membermanager.py
- container_job: netgroup
tests:
- test_integration/test_netgroup.py
- container_job: external_ca_ExternalCAProfileScenarios
tests:
- test_integration/test_external_ca.py::TestExternalCAProfileScenarios
- vm_jobs:
- container_job: ServerReplicaCALessToCAFull
tests:
+14 -8
View File
@@ -12,7 +12,7 @@ from jinja2 import Template
from typing import NamedTuple, TYPE_CHECKING
if TYPE_CHECKING:
from typing import List, Tuple, Union
from typing import List, Tuple, Union, Dict
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
@@ -77,14 +77,19 @@ class Container:
return self._ipv6
def execute(
self, args: Union[str, List[str]], raiseonerr: bool = True
self,
args: Union[str, List[str]],
raiseonerr: bool = True,
env: Union[Dict[str, str], List[str], None] = None,
) -> ExecRunReturn:
"""
Exec an arbitrary command within container
"""
dcont = self.dclient.containers.get(self.name)
logging.info("%s: run: %s", dcont.name, args)
result: ExecRunReturn = dcont.exec_run(args, demux=True)
result: ExecRunReturn = dcont.exec_run(
args, demux=True, environment=env
)
if result.output[0] is not None:
logging.info("%s: %s", dcont.name, result.output[0])
logging.info("%s: result: %s", dcont.name, result.exit_code)
@@ -127,13 +132,13 @@ class ContainersGroup:
for c in range(1, self.scale + 1)
]
def execute_all(self, args):
def execute_all(self, args, env=None):
"""
Sequentially exec an arbitrary command within every container of group
"""
results = []
for cont in self.containers:
results.append(cont.execute(args))
results.append(cont.execute(args, env=env))
return results
def ips(self):
@@ -199,7 +204,8 @@ class ContainersGroup:
cont.execute(cmd)
cmd = ["hostnamectl", "set-hostname", cont.hostname]
cont.execute(cmd)
# default timeout (25s) maybe not enough
cont.execute(cmd, env={"SYSTEMD_BUS_TIMEOUT": "50"})
def setup_resolvconf(self):
"""
@@ -331,7 +337,7 @@ class Controller(Container):
return self._master_container
def execute(self, args):
def execute(self, args, env=None):
"""
Execute a command on controller (either master or local machine)
"""
@@ -339,7 +345,7 @@ class Controller(Container):
proc = subprocess.run(args, check=True, capture_output=True)
return [proc.stdout.decode().rstrip().strip("'")]
return self.master_container.execute(args)
return self.master_container.execute(args, env=env)
def setup_hosts(self):
"""
+30
View File
@@ -4,6 +4,11 @@ steps:
env | sort
displayName: Print Host Enviroment
- script: |
set -e
sudo apt list --installed
displayName: Show Host's installed packages
- script: |
set -e
sudo apt-get update
@@ -16,6 +21,21 @@ steps:
python3-docker
displayName: Install Host's tests requirements
- script: |
set -e
sudo systemctl
displayName: Show Host's systemd status
- script: |
set -e
# most of the time systemd killed hostnamed with SIGKILL on timeout
# kill without waiting for graceful termination
sudo systemctl kill -s SIGKILL azsecd ||:
sudo systemctl disable --now azsecmond ||:
sudo systemctl disable --now azsecd ||:
sudo systemctl disable --now clamav-freshclam ||:
displayName: Disable azsec services (clamav)
- script: |
set -e
printf "AppArmor status\n"
@@ -52,6 +72,16 @@ steps:
- template: setup-test-environment.yml
- script: |
set -eu
sudo top -b -o +%MEM n 1
displayName: Show Host's top
- script: |
set -eu
sudo ps -auxf
displayName: Show Host's processes
- template: run-test.yml
- script: |
+1 -1
View File
@@ -309,4 +309,4 @@ jobs:
test_suite: test_integration/test_subids.py
template: *ci-ipa-4-9-latest
timeout: 3600
topology: *master_1repl
topology: *master_1repl_1client
@@ -1748,7 +1748,7 @@ jobs:
test_suite: test_integration/test_subids.py
template: *ci-ipa-4-9-latest
timeout: 3600
topology: *master_1repl
topology: *master_1repl_1client
fedora-latest-ipa-4-9/test_custom_plugins:
requires: [fedora-latest-ipa-4-9/build]
@@ -1771,5 +1771,5 @@ jobs:
build_url: '{fedora-latest-ipa-4-9/build_url}'
test_suite: test_integration/test_idp.py
template: *ci-ipa-4-9-latest
timeout: 3600
topology: *master_1repl
timeout: 5000
topology: *master_2repl_1client
@@ -1887,7 +1887,7 @@ jobs:
test_suite: test_integration/test_subids.py
template: *ci-ipa-4-9-latest
timeout: 3600
topology: *master_1repl
topology: *master_1repl_1client
fedora-latest-ipa-4-9/test_custom_plugins:
requires: [fedora-latest-ipa-4-9/build]
@@ -1912,5 +1912,5 @@ jobs:
selinux_enforcing: True
test_suite: test_integration/test_idp.py
template: *ci-ipa-4-9-latest
timeout: 3600
topology: *master_1repl
timeout: 5000
topology: *master_2repl_1client
@@ -1748,7 +1748,7 @@ jobs:
test_suite: test_integration/test_subids.py
template: *ci-ipa-4-9-previous
timeout: 3600
topology: *master_1repl
topology: *master_1repl_1client
fedora-previous-ipa-4-9/test_custom_plugins:
requires: [fedora-previous-ipa-4-9/build]
@@ -1771,5 +1771,5 @@ jobs:
build_url: '{fedora-previous-ipa-4-9/build_url}'
test_suite: test_integration/test_idp.py
template: *ci-ipa-4-9-previous
timeout: 3600
topology: *master_1repl
timeout: 5000
topology: *master_2repl_1client
+433
View File
@@ -0,0 +1,433 @@
#! /usr/bin/python3
import os
import glob
import sys
import argparse
from argparse import RawTextHelpFormatter
import yaml
# Get default DIR from script location
DEFAULT_DIR = os.path.dirname(os.path.abspath(__file__))
# Default jobs specification file name and path
JOBS_SPEC_YAML = "prci_jobs_spec.yaml"
JOBS_SPEC_PATH = os.path.join(DEFAULT_DIR, JOBS_SPEC_YAML)
# Files to ignore on check
IGNORE_FILES = {JOBS_SPEC_YAML, "temp_commit.yaml"}
def load_yaml(path):
"""Load YAML file into Python object."""
with open(path, "r") as file_data:
data = yaml.safe_load(file_data)
return data
def print_error(msg):
"""Helper function to print error messages"""
print("ERROR: " + msg)
def print_warning(msg):
"""Helper function to print warning messages"""
print("WARNING: " + msg)
def print_hint(msg):
"""Helper function to print hint messages"""
print("HINT: " + msg)
def print_field_error(
jobname, fieldname=None, expected_value=None, custom_msg=None
):
"""Helper function to print field errors"""
msg = f"In job '{jobname}':\n"
if custom_msg:
msg += f" {custom_msg}"
elif fieldname and expected_value:
msg += (
f' Job field "{fieldname}" should be defined as: '
f'"{fieldname}: {expected_value}"'
)
else:
msg = f"In job '{jobname}'."
print_error(msg)
def check_jobs(filename, jobs_def, topologies, current_spec, supported_classes):
"""
Check if given job definition file has all jobs correctly defined according
to specification file.
:param filename: file name of the definition file to be checked
:param jobs_def: definition file jobs as a dict object
:param topologies: list of dicts of predefined topologies
:param jobs_spec: PRCI specification file containing correct definitions
:param supported_classes: List of supported test-run classes
:returns: Boolean with the checks result
"""
correct_fields = True
try:
job_prefix = current_spec["job_prefix"]
except KeyError as e:
print_error(
"Specification file has bad format "
f"and '{filename}' could not be analyzed.\n"
f" KeyError: {e} in '{filename}'"
)
return False
requires = [f"{job_prefix}build"]
build_url = f"{{{job_prefix}build_url}}"
# Get template from build job
build_job_name = job_prefix + "build"
build_job = jobs_def.get(build_job_name)
if not build_job:
print_error(
" Build job is not defined or has incorrect name.\n"
f" Name should be: '{build_job_name}'"
)
return False
build_args = build_job["job"]["args"]
template = build_args["template"]
copr = build_args.get("copr")
copr_defined = current_spec.get("copr_defined", False)
update_packages = current_spec.get("update_packages", False)
selinux = current_spec.get("selinux_enforcing", False)
enable_testing_repo = current_spec.get("enable_testing_repo", False)
for job_name, params in jobs_def.items():
# Checks for all kind of jobs
args = params.get("job").get("args")
if not job_name.startswith(job_prefix):
msg = f"Job name should start with prefix '{job_prefix}'"
print_field_error(job_name, custom_msg=msg)
correct_fields = False
if args.get("template") != template:
print_field_error(job_name, "template", template)
correct_fields = False
if "timeout" not in args:
msg = "'timeout' field should be defined in args section"
print_field_error(job_name, custom_msg=msg)
if args.get("topology") not in topologies:
msg = (
"'topology' field should be defined with one of the "
"pre-defined topologies"
)
print_field_error(job_name, custom_msg=msg)
correct_fields = False
if args.get("enable_testing_repo", False) != enable_testing_repo:
if enable_testing_repo:
print_field_error(
job_name, "enable_testing_repo", enable_testing_repo
)
else:
msg = (
"'enable_testing_repo' field should be set to false or not"
" defined"
)
print_field_error(job_name, custom_msg=msg)
correct_fields = False
# Checks for build job
if job_name == build_job_name:
if copr_defined and not copr:
msg = "'copr' field should be defined for the build job"
print_field_error(job_name, custom_msg=msg)
correct_fields = False
elif not copr_defined and copr:
msg = "'copr' field should NOT be defined for the build job"
print_field_error(job_name, custom_msg=msg)
correct_fields = False
if params.get("job").get("class") != "Build":
print_field_error(job_name, "class", "Build")
correct_fields = False
continue
# Checks only for non-build jobs
if params.get("requires") != requires:
print_field_error(job_name, "requires", requires)
correct_fields = False
if params.get("job").get("class") not in supported_classes:
msg = (
"'class' field should be defined with one of the "
f"supported: {supported_classes}"
)
print_field_error(job_name, custom_msg=msg)
correct_fields = False
if args.get("build_url") != build_url:
print_field_error(job_name, "build_url", f"'{build_url}'")
correct_fields = False
if "test_suite" not in args:
msg = "'test_suite' field should be defined in args section"
print_field_error(job_name, custom_msg=msg)
correct_fields = False
# Check template field against build target
if args.get("template") != template:
print_field_error(job_name, "template", template)
correct_fields = False
if "timeout" not in args:
msg = "'timeout' field should be defined in args section"
print_field_error(job_name, custom_msg=msg)
correct_fields = False
# If build target has a copr repo, check that the job also defines it
if args.get("copr") != copr:
if copr and copr_defined:
print_field_error(job_name, "copr", copr)
elif not copr and not copr_defined:
msg = "'copr' field should not be defined"
print_field_error(job_name, custom_msg=msg)
correct_fields = False
if args.get("update_packages", False) != update_packages:
if update_packages:
print_field_error(job_name, "update_packages", update_packages)
else:
msg = (
"'update_packages' field should be set to false or not"
" defined"
)
print_field_error(job_name, custom_msg=msg)
correct_fields = False
if args.get("selinux_enforcing", False) != selinux:
if selinux:
print_field_error(job_name, "selinux_enforcing", selinux)
else:
msg = (
"'selinux_enforcing' field should be set to false or not"
" defined"
)
print_field_error(job_name, custom_msg=msg)
correct_fields = False
return correct_fields
def process_def_file(file, jobs_spec, supported_classes):
"""Function to process PRCI definition file
:param file: name of the definition file to be
processed (extension included)
:param jobs_spec: PRCI specification file containing correct definitions
:param supported_classes: List of supported test-run classes
:returns: Boolean with the checks result, filename,
and number of jobs in the definition
file (-1 when error / warning)
"""
# File base name without extension
filename = os.path.splitext(os.path.basename(file))[0]
try:
def_suite = load_yaml(file)
except FileNotFoundError as e:
print(e)
print_error(f"File '{file}' was not found.")
sys.exit(1)
except yaml.composer.ComposerError as e:
print_error(str(e))
print_hint(
"You probably defined a wrong alias "
"in the newly added or modified job."
)
sys.exit(1)
except yaml.YAMLError as e:
print(e)
print_error(f"Error loading YAML definition file {file}")
sys.exit(1)
# Get spec for file to be analyzed
current_spec = jobs_spec.get(filename)
if current_spec is None:
print_warning(
f"'{filename}' file is not defined in the PRCI "
"specification file and "
"could not be analyzed."
)
return True, "", -1
jobs_def = def_suite.get("jobs")
if jobs_def is None:
print_error(
f"'{filename}' file doesn't have a jobs section following "
"the format."
)
return False, "", -1
# Get list of pre-defined topologies
topologies_def = def_suite.get("topologies")
if topologies_def is None:
print_error(
f"'{filename}' file doesn't have a topologies section following "
"the format."
)
return False, "", -1
topologies = list(topologies_def.values())
# Print file to be analyzed and its number of jobs
n_jobs = len(jobs_def)
print("[File] " + filename + " [Jobs] " + str(n_jobs))
result = check_jobs(
filename, jobs_def, topologies, current_spec, supported_classes
)
return result, filename, n_jobs
def process_spec_file(filepath):
"""Function to process jobs specification file
:param filepath: Filepath for spec file
:returns: Definition specification dict, supported classes and
list of files that should contain the same number of jobs
"""
try:
spec_root = load_yaml(filepath)
except FileNotFoundError as e:
print(e)
print_error(f"Jobs specification file '{filepath}' not found.")
sys.exit(1)
except yaml.YAMLError as e:
print(e)
print_error(f"Error loading YAML specification file '{filepath}'")
sys.exit(1)
jobs_spec = spec_root.get("prci_job_spec")
if not jobs_spec:
print_error(
f"Specification definition not found in spec file '{filepath}'\n"
" Key 'prci_job_spec' is not present."
)
sys.exit(1)
supported_classes = spec_root.get("classes")
if not supported_classes:
print_error(
f"Supported classes not defined in spec file '{filepath}'\n"
" Key 'classes' is not present."
)
sys.exit(1)
f_fixed_jobs = spec_root.get("fixed_n_jobs")
return jobs_spec, supported_classes, f_fixed_jobs
def check_n_jobs(defs_n_jobs):
"""
Function to check if definition files have the same number of jobs
:param defs_n_jobs: Dict of definition filenames as keys and number
of jobs as values
:returns: Boolean, if definitions have the same number of jobs
"""
if not defs_n_jobs: # Spec not defined to check num of jobs
return True
elif len(set(defs_n_jobs.values())) == 1:
return True
else:
print_error(
"Following PRCI definitions should have the same number of jobs:"
f" {list(defs_n_jobs.keys())}"
)
return False
def parse_arguments(description):
"""Parse and return arguments if specified"""
parser = argparse.ArgumentParser(
description=description, formatter_class=RawTextHelpFormatter
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-f", "--file", help="Specify YAML definition file to be analyzed"
)
group.add_argument(
"-d",
"--defs",
default=DEFAULT_DIR,
help="Specify directory for definition files to be analyzed",
)
parser.add_argument(
"-s",
"--spec",
default=JOBS_SPEC_PATH,
help="Specify path for specification file",
)
return parser.parse_args()
def main():
"""
Checker script for prci definition files.\n
This script checks whether jobs in a prci definition file have the correct
naming format, requirements, and arguments, which are defined in the
specification file.
If no defition file, definition directory or spec file is specified,
script will look for them in its own dir location.
Examples of the usage for the tool:\n
# Check all yaml definition files in default dir\n
python3 prci_checker.py\n
# Check only specified file\n
python3 prci_checker.py -f gating.yaml\n
# Check with custom path for spec file\n
python3 prci_checker.py -s ../../alternative_spec.yaml
# Check with custom path for spec file\n
python3 prci_checker.py -d ./definitions
"""
args = parse_arguments(main.__doc__)
print("BEGINNING PRCI JOB DEFINITIONS CHECKS")
# Get data from jobs specification file
jobs_spec, supported_classes, f_fixed_jobs = process_spec_file(args.spec)
if args.file:
result = process_def_file(args.file, jobs_spec, supported_classes)
else:
# Get all yaml files in default dir, except those in IGNORE_FILES
def_files_dir = os.path.join(args.defs, "*.y*ml")
defs_files = glob.glob(def_files_dir)
ignore_files_paths = {
os.path.join(args.defs, ignore_file) for ignore_file in IGNORE_FILES
}
defs_files = set(defs_files) - ignore_files_paths
if not defs_files:
print_warning(
"No yaml job definition files found to analyze "
"in specified directory."
)
return
result = True
defs_n_jobs = {}
for def_file in defs_files:
result_file, filename, n_jobs = process_def_file(
def_file, jobs_spec, supported_classes
)
if not result_file:
result = False
continue
if n_jobs > -1 and f_fixed_jobs and filename in f_fixed_jobs:
defs_n_jobs[filename] = n_jobs
result = result and check_n_jobs(defs_n_jobs)
if not result:
print("CHECKS FINISHED WITH ERRORS")
sys.exit(1)
print("CHECKS FINISHED SUCCESSFULLY")
if __name__ == "__main__":
main()
@@ -0,0 +1,32 @@
# Specification file for PRCI definitions used by prci_checker script
# List of supported test-run classes for non-build jobs
classes:
- "RunPytest"
- "RunPytest2"
- "RunPytest3"
- "RunWebuiTests"
- "RunADTests"
# (Optional) Definition files that should contain the same number of jobs
fixed_n_jobs:
- nightly_ipa-4-9_latest.yaml
- nightly_ipa-4-9_latest_selinux.yaml
- nightly_ipa-4-9_latest_previous.yaml
# Info specific to prci definition files
# 'job_prefix' field is mandatory
# 'update_packages', 'selinux_enforcing', 'enable_testing_repo' and
# 'copr_defined' are optional boolean fields (if not specified,
# false value is assumed).
# New definitions specifications may be added anytime
prci_job_spec:
gating:
job_prefix: 'fedora-latest-ipa-4-9/'
nightly_ipa-4-9_latest:
job_prefix: 'fedora-latest-ipa-4-9/'
nightly_ipa-4-9_latest_selinux:
job_prefix: 'fedora-latest-ipa-4-9/'
selinux_enforcing: True
nightly_ipa-4-9_previous:
job_prefix: 'fedora-previous-ipa-4-9/'
+226 -34
View File
@@ -1,6 +1,8 @@
from __future__ import absolute_import
import time
import pytest
import re
import textwrap
from ipaplatform.paths import paths
@@ -22,12 +24,12 @@ driver.get(verification_uri)
try:
element = WebDriverWait(driver, 90).until(
EC.presence_of_element_located((By.ID, "username")))
driver.find_element_by_id("username").send_keys("testuser1")
driver.find_element_by_id("password").send_keys("{passwd}")
driver.find_element_by_id("kc-login").click()
driver.find_element(By.ID, "username").send_keys("testuser1")
driver.find_element(By.ID, "password").send_keys("{passwd}")
driver.find_element(By.ID, "kc-login").click()
element = WebDriverWait(driver, 90).until(
EC.presence_of_element_located((By.ID, "kc-login")))
driver.find_element_by_id("kc-login").click()
driver.find_element(By.ID, "kc-login").click()
assert "Device Login Successful" in driver.page_source
finally:
now = datetime.now().strftime("%M-%S")
@@ -39,18 +41,12 @@ finally:
def add_user_code(host, verification_uri):
contents = user_code_script.format(uri=verification_uri,
passwd=host.config.admin_password)
host.put_file_contents("/tmp/add_user_code.py", contents)
tasks.run_repeatedly(
host, ['python3', '/tmp/add_user_code.py'])
def get_verification_uri(host, since, keycloak_server_name):
command = textwrap.dedent("""
journalctl -u ipa-otpd\\* --since="%s" | grep "user_code:" | awk '{ print substr($7,2,9) }'""" % since) # noqa: E501
user_code = host.run_command(command).stdout_text.rstrip("\r\n")
uri = ("https://{0}:8443/auth/realms/master/device?user_code={1}".format(
keycloak_server_name, user_code))
return uri
try:
host.put_file_contents("/tmp/add_user_code.py", contents)
tasks.run_repeatedly(
host, ['python3', '/tmp/add_user_code.py'])
finally:
host.run_command(["rm", "-f", "/tmp/add_user_code.py"])
def kinit_idp(host, user, keycloak_server):
@@ -58,11 +54,14 @@ def kinit_idp(host, user, keycloak_server):
tasks.kdestroy_all(host)
# create armor for FAST
host.run_command(["kinit", "-n", "-c", ARMOR])
since = time.strftime('%Y-%m-%d %H:%M:%S')
cmd = ["kinit", "-T", ARMOR, user]
with host.spawn_expect(cmd, default_timeout=100) as e:
e.expect('Authenticate at .+: ')
uri = get_verification_uri(host, since, keycloak_server.hostname)
e.expect('Authenticate at (.+) and press ENTER.:')
prompt = e.get_last_output()
uri = re.search(r'Authenticate at (.*?) and press ENTER.:', prompt
).group(1)
time.sleep(15)
if uri:
add_user_code(keycloak_server, uri)
e.sendline('\n')
@@ -74,21 +73,27 @@ def kinit_idp(host, user, keycloak_server):
class TestIDPKeycloak(IntegrationTest):
num_replicas = 1
num_replicas = 2
topology = 'line'
@classmethod
def install(cls, mh):
tasks.install_master(cls.master, setup_dns=True)
tasks.install_client(cls.master, cls.replicas[0])
content = cls.master.get_file_contents(paths.IPA_DEFAULT_CONF,
encoding='utf-8')
new_content = content + "\noidc_child_debug_level = 10"
cls.master.put_file_contents(paths.IPA_DEFAULT_CONF, new_content)
cls.client = cls.replicas[0]
cls.replica = cls.replicas[1]
tasks.install_master(cls.master, extra_args=['--no-dnssec-validation'])
tasks.install_client(cls.master, cls.replicas[0],
extra_args=["--mkhomedir"])
tasks.install_replica(cls.master, cls.replicas[1])
for host in [cls.master, cls.replicas[0], cls.replicas[1]]:
content = host.get_file_contents(paths.IPA_DEFAULT_CONF,
encoding='utf-8')
new_content = content + "\noidc_child_debug_level = 10"
host.put_file_contents(paths.IPA_DEFAULT_CONF, new_content)
with tasks.remote_sssd_config(cls.master) as sssd_config:
sssd_config.edit_domain(
cls.master.domain, 'krb5_auth_timeout', 1100)
tasks.clear_sssd_cache(cls.master)
tasks.clear_sssd_cache(cls.replicas[0])
tasks.kinit_admin(cls.master)
cls.master.run_command(["ipa", "config-mod", "--user-auth-type=idp",
"--user-auth-type=password"])
@@ -97,20 +102,207 @@ class TestIDPKeycloak(IntegrationTest):
cls.replicas[0].run_command(xvfb)
def test_auth_keycloak_idp(self):
keycloak_srv = self.replicas[0]
create_quarkus.setup_keycloakserver(keycloak_srv)
"""
Test case to check that OAuth 2.0 Device
Authorization Grant is working as
expected for user configured with external idp.
"""
create_quarkus.setup_keycloakserver(self.client)
time.sleep(60)
create_quarkus.setup_keycloak_client(keycloak_srv)
create_quarkus.setup_keycloak_client(self.client)
tasks.kinit_admin(self.master)
cmd = ["ipa", "idp-add", "keycloak", "--provider=keycloak",
cmd = ["ipa", "idp-add", "keycloakidp", "--provider=keycloak",
"--client-id=ipa_oidc_client", "--org=master",
"--base-url={0}:8443/auth".format(keycloak_srv.hostname)]
"--base-url={0}:8443/auth".format(self.client.hostname)]
self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
keycloak_srv.config.admin_password))
self.client.config.admin_password))
tasks.user_add(self.master, 'keycloakuser',
extra_args=["--user-auth-type=idp",
"--idp-user-id=testuser1@ipa.test",
"--idp=keycloak"]
"--idp=keycloakidp"]
)
list_user = self.master.run_command(
["ipa", "user-find", "--idp-user-id=testuser1@ipa.test"]
)
assert "keycloakuser" in list_user.stdout_text
list_by_idp = self.master.run_command(["ipa", "user-find",
"--idp=keycloakidp"]
)
assert "keycloakuser" in list_by_idp.stdout_text
list_by_user = self.master.run_command(
["ipa", "user-find", "--idp-user-id=testuser1@ipa.test", "--all"]
)
assert "keycloakidp" in list_by_user.stdout_text
tasks.clear_sssd_cache(self.master)
kinit_idp(self.master, 'keycloakuser', keycloak_srv)
kinit_idp(self.master, 'keycloakuser', keycloak_server=self.client)
@pytest.fixture
def hbac_setup_teardown(self):
# allow sshd only on given host
tasks.kinit_admin(self.master)
self.master.run_command(["ipa", "hbacrule-disable", "allow_all"])
self.master.run_command(["ipa", "hbacrule-add", "rule1"])
self.master.run_command(["ipa", "hbacrule-add-user", "rule1",
"--users=keycloakuser"]
)
self.master.run_command(["ipa", "hbacrule-add-host", "rule1",
"--hosts", self.replica.hostname])
self.master.run_command(["ipa", "hbacrule-add-service", "rule1",
"--hbacsvcs=sshd"]
)
tasks.clear_sssd_cache(self.master)
tasks.clear_sssd_cache(self.replica)
yield
# cleanup
tasks.kinit_admin(self.master)
self.master.run_command(["ipa", "hbacrule-enable", "allow_all"])
self.master.run_command(["ipa", "hbacrule-del", "rule1"])
def test_auth_hbac(self, hbac_setup_teardown):
"""
Test case to check that hbacrule is working as
expected for user configured with external idp.
"""
kinit_idp(self.master, 'keycloakuser', keycloak_server=self.client)
ssh_cmd = "ssh -q -K -l keycloakuser {0} whoami"
valid_ssh = self.master.run_command(
ssh_cmd.format(self.replica.hostname))
assert "keycloakuser" in valid_ssh.stdout_text
negative_ssh = self.master.run_command(
ssh_cmd.format(self.master.hostname), raiseonerr=False
)
assert negative_ssh.returncode == 255
def test_auth_sudo_idp(self):
"""
Test case to check that sudorule is working as
expected for user configured with external idp.
"""
tasks.kdestroy_all(self.master)
tasks.kinit_admin(self.master)
# rule: keycloakuser are allowed to execute yum on
# the client machine as root.
cmdlist = [
["ipa", "sudocmd-add", "/usr/bin/yum"],
["ipa", "sudorule-add", "sudorule"],
['ipa', 'sudorule-add-user', '--users=keycloakuser',
'sudorule'],
['ipa', 'sudorule-add-host', '--hosts',
self.client.hostname, 'sudorule'],
['ipa', 'sudorule-add-runasuser',
'--users=root', 'sudorule'],
['ipa', 'sudorule-add-allow-command',
'--sudocmds=/usr/bin/yum', 'sudorule'],
['ipa', 'sudorule-show', 'sudorule', '--all'],
['ipa', 'sudorule-add-option',
'sudorule', '--sudooption', "!authenticate"]
]
for cmd in cmdlist:
self.master.run_command(cmd)
tasks.clear_sssd_cache(self.master)
tasks.clear_sssd_cache(self.client)
try:
cmd = 'sudo -ll -U keycloakuser'
test = self.client.run_command(cmd).stdout_text
assert "User keycloakuser may run the following commands" in test
assert "/usr/bin/yum" in test
kinit_idp(self.client, 'keycloakuser', self.client)
test_sudo = 'su -c "sudo yum list wget" keycloakuser'
self.client.run_command(test_sudo)
list_fail = self.master.run_command(cmd).stdout_text
assert "User keycloakuser is not allowed to run sudo" in list_fail
finally:
tasks.kinit_admin(self.master)
self.master.run_command(['ipa', 'sudorule-del', 'sudorule'])
self.master.run_command(["ipa", "sudocmd-del", "/usr/bin/yum"])
def test_auth_replica(self):
"""
Test case to check that OAuth 2.0 Device
Authorization is working as expected on replica.
"""
tasks.clear_sssd_cache(self.master)
tasks.clear_sssd_cache(self.replica)
tasks.kinit_admin(self.replica)
list_user = self.master.run_command(
["ipa", "user-find", "--idp-user-id=testuser1@ipa.test"]
)
assert "keycloakuser" in list_user.stdout_text
list_by_idp = self.replica.run_command(["ipa", "user-find",
"--idp=keycloakidp"]
)
assert "keycloakuser" in list_by_idp.stdout_text
list_by_user = self.replica.run_command(
["ipa", "user-find", "--idp-user-id=testuser1@ipa.test", "--all"]
)
assert "keycloakidp" in list_by_user.stdout_text
kinit_idp(self.replica, 'keycloakuser', keycloak_server=self.client)
def test_idp_with_services(self):
"""
Test case to check that services can be configured
auth indicator as idp.
"""
tasks.clear_sssd_cache(self.master)
tasks.kinit_admin(self.master)
domain = self.master.domain.name.upper()
services = [
"DNS/{0}@{1}".format(self.master.hostname, domain),
"HTTP/{0}@{1}".format(self.client.hostname, domain),
"dogtag/{0}@{1}".format(self.master.hostname, domain),
"ipa-dnskeysyncd/{0}@{1}".format(self.master.hostname, domain)
]
try:
for service in services:
test = self.master.run_command(["ipa", "service-mod", service,
"--auth-ind=idp"]
)
assert "Authentication Indicators: idp" in test.stdout_text
finally:
for service in services:
self.master.run_command(["ipa", "service-mod", service,
"--auth-ind="])
def test_idp_backup_restore(self):
"""
Test case to check that after restore data is retrieved
with related idp configuration.
"""
tasks.kinit_admin(self.master)
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)]
self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
self.client.config.admin_password))
tasks.user_add(self.master, user,
extra_args=["--user-auth-type=idp",
"--idp-user-id=testuser1@ipa.test",
"--idp=testidp"]
)
backup_path = tasks.get_backup_dir(self.master)
# change data after backup
self.master.run_command(['ipa', 'user-del', user])
self.master.run_command(['ipa', 'idp-del', 'testidp'])
dirman_password = self.master.config.dirman_password
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
try:
list_user = self.master.run_command(
['ipa', 'user-show', 'backupuser', '--all']
).stdout_text
assert "External IdP configuration: testidp" in list_user
assert "User authentication types: idp" in list_user
assert ("External IdP user identifier: "
"testuser1@ipa.test") in list_user
list_idp = self.master.run_command(['ipa', 'idp-find', 'testidp'])
assert "testidp" in list_idp.stdout_text
kinit_idp(self.master, user, self.client)
finally:
tasks.kdestroy_all(self.master)
tasks.kinit_admin(self.master)
self.master.run_command(["rm", "-rf", backup_path])
self.master.run_command(["ipa", "idp-del", "testidp"])
@@ -881,7 +881,7 @@ class TestInstallMaster(IntegrationTest):
"""
# check process count in httpd conf file i.e expected string
exp = b'WSGIDaemonProcess ipa processes=%d' % constants.WSGI_PROCESSES
httpd_conf = self.master.get_file_contents(paths.HTTPD_IPA_CONF)
httpd_conf = self.master.get_file_contents(paths.HTTPD_IPA_WSGI_CONF)
assert exp in httpd_conf
# check the process count
@@ -1206,7 +1206,7 @@ class TestInstallMaster(IntegrationTest):
try:
cmd = ['ipa-server-install', '--hostname', new_hostname]
with self.master.spawn_expect(cmd) as e:
with self.master.spawn_expect(cmd, default_timeout=100) as e:
e.expect_exact('Do you want to configure integrated '
'DNS (BIND)? [no]: ')
e.sendline('no')
@@ -1417,7 +1417,7 @@ class TestInstallMasterDNS(IntegrationTest):
"""
cmd = ['ipa-server-install']
netbios = create_netbios_name(self.master)
with self.master.spawn_expect(cmd) as e:
with self.master.spawn_expect(cmd, default_timeout=100) as e:
e.expect_exact('Do you want to configure integrated '
'DNS (BIND)? [no]: ')
e.sendline('yes')
@@ -340,6 +340,31 @@ class TestIpaHealthCheck(IntegrationTest):
assert returncode == 0
assert output == "No issues found."
def test_ipa_healthcheck_fips_enabled(self):
"""
Test if FIPS is enabled and the check exists.
https://pagure.io/freeipa/issue/8951
"""
returncode, check = run_healthcheck(self.master,
source="ipahealthcheck.meta.core",
check="MetaCheck",
output_type="json",
failures_only=False)
assert returncode == 0
cmd = self.master.run_command(['fips-mode-setup', '--is-enabled'],
raiseonerr=False)
returncode = cmd.returncode
# If this produces IndexError, the check does not exist
if check[0]["kw"]["fips"] == "disabled":
assert returncode == 2
elif check[0]["kw"]["fips"] == "enabled":
assert returncode == 0
else:
assert returncode == 1
def test_ipa_healthcheck_after_certupdate(self):
"""
Verify that ipa-certupdate hasn't messed up tracking
@@ -507,6 +532,45 @@ class TestIpaHealthCheck(IntegrationTest):
assert additional_msg in check["kw"]["msg"] or \
additional_msg == check["kw"]["error"]
def test_ipahealthcheck_ca_not_configured(self):
"""
Test if the healthcheck ignores pki-tomcat errors
when CA is not configured on the machine
Related: https://github.com/freeipa/freeipa-healthcheck/issues/201
"""
# uninstall replica installed by class' install method
tasks.uninstall_replica(self.master, self.replicas[0])
# install it again without CA
tasks.install_replica(self.master,
self.replicas[0],
setup_ca=False,
setup_dns=True,
extra_args=['--no-dnssec-validation']
)
# Init a user on replica to assign a DNA range
tasks.kinit_admin(self.replicas[0])
tasks.user_add(
self.replicas[0], 'ipauser1', first='Test', last='User',
)
returncode, data = run_healthcheck(self.replicas[0],
failures_only=True)
assert returncode == 0
assert len(data) == 0
# restore the replica original configuration
tasks.user_del(self.replicas[0], 'ipauser1')
tasks.uninstall_replica(self.master, self.replicas[0])
tasks.install_replica(
self.master,
self.replicas[0],
setup_dns=True,
extra_args=['--no-dnssec-validation']
)
def test_source_ipahealthcheck_meta_core_metacheck(self):
"""
Testcase checks behaviour of check MetaCheck in source
@@ -2843,3 +2907,65 @@ class TestIpaHealthCheckWithExternalCA(IntegrationTest):
assert check["kw"]["msg"] == error_msg
else:
assert error_reason in check["kw"]["msg"]
class TestIpaHealthCheckSingleMaster(IntegrationTest):
@classmethod
def install(cls, mh):
# Nota Bene: The ipa server is not installed
tasks.install_packages(cls.master, HEALTHCHECK_PKG)
def test_ipahealthcheck_mismatching_certificates_subject(self):
"""
Test if healthcheck uses cert subject base from IPA and not from
REALM. This prevents false-positive errors when the subject base is
customized.
Related: https://github.com/freeipa/freeipa-healthcheck/issues/253
"""
# install master with custom cert subject base
tasks.install_master(
self.master,
setup_dns=True,
extra_args=[
'--no-dnssec-validation',
'--subject-base=O=LINUX.IS.GREAT,C=EU'
]
)
try:
returncode, data = run_healthcheck(
self.master,
source="ipahealthcheck.ipa.certs",
check="IPADogtagCertsMatchCheck",
failures_only=True)
assert returncode == 0
assert len(data) == 0
finally:
# uninstall server for the next step
tasks.uninstall_master(self.master)
# install master with custom CA certificate subject DN
tasks.install_master(
self.master,
setup_dns=True,
extra_args=[
'--no-dnssec-validation',
'--ca-subject=CN=Healthcheck test,O=LINUX.IS.GREAT'
]
)
try:
returncode, data = run_healthcheck(
self.master,
source="ipahealthcheck.ipa.certs",
check="IPADogtagCertsMatchCheck",
failures_only=True)
assert returncode == 0
assert len(data) == 0
finally:
# cleanup
tasks.uninstall_master(self.master)
+52 -3
View File
@@ -36,7 +36,7 @@ class TestPWPolicy(IntegrationTest):
cls.master.run_command(['ipa', 'group-add-member', POLICY,
'--users', USER])
cls.master.run_command(['ipa', 'pwpolicy-add', POLICY,
'--priority', '1'])
'--priority', '1', '--gracelimit', '-1'])
cls.master.run_command(['ipa', 'passwd', USER],
stdin_text='{password}\n{password}\n'.format(
password=PASSWORD
@@ -265,7 +265,6 @@ class TestPWPolicy(IntegrationTest):
def test_graceperiod_expired(self):
"""Test the LDAP bind grace period"""
str(self.master.domain.basedn)
dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
user=USER, base_dn=str(self.master.domain.basedn))
@@ -308,7 +307,6 @@ class TestPWPolicy(IntegrationTest):
def test_graceperiod_not_replicated(self):
"""Test that the grace period is reset on password reset"""
str(self.master.domain.basedn)
dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
user=USER, base_dn=str(self.master.domain.basedn))
@@ -341,3 +339,54 @@ class TestPWPolicy(IntegrationTest):
)
assert 'passwordgraceusertime: 0' in result.stdout_text.lower()
self.reset_password(self.master)
def test_graceperiod_zero(self):
"""Test the LDAP bind with zero grace period"""
dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
user=USER, base_dn=str(self.master.domain.basedn))
self.master.run_command(
["ipa", "pwpolicy-mod", POLICY, "--gracelimit", "0", ],
)
# Resetting the password will mark it as expired
self.reset_password(self.master)
# Now grace is done and binds should fail.
result = self.master.run_command(
["ldapsearch", "-e", "ppolicy", "-D", dn,
"-w", PASSWORD, "-b", dn], raiseonerr=False
)
assert result.returncode == 49
assert 'Password is expired' in result.stderr_text
assert 'Password expired, 0 grace logins remain' in result.stderr_text
def test_graceperiod_disabled(self):
"""Test the LDAP bind with grace period disabled (-1)"""
str(self.master.domain.basedn)
dn = "uid={user},cn=users,cn=accounts,{base_dn}".format(
user=USER, base_dn=str(self.master.domain.basedn))
# This can fail if gracelimit is already -1 so ignore it
self.master.run_command(
["ipa", "pwpolicy-mod", POLICY, "--gracelimit", "-1",],
raiseonerr=False,
)
# Ensure the password is expired
self.reset_password(self.master)
result = self.kinit_as_user(self.master, PASSWORD, PASSWORD)
for _i in range(0, 10):
result = self.master.run_command(
["ldapsearch", "-e", "ppolicy", "-D", dn,
"-w", PASSWORD, "-b", dn]
)
# With graceperiod disabled it should not increment
result = tasks.ldapsearch_dm(
self.master, dn, ['passwordgraceusertime',],
)
assert 'passwordgraceusertime: 0' in result.stdout_text.lower()
+38
View File
@@ -17,6 +17,7 @@ from ipatests.test_integration.base import IntegrationTest
class TestSubordinateId(IntegrationTest):
num_replicas = 0
num_clients = 1
topology = "star"
def _parse_result(self, result):
@@ -268,3 +269,40 @@ class TestSubordinateId(IntegrationTest):
f"--subuid={subuid}"])
owner = self._parse_result(result)["owner"]
assert owner == uid
def test_nsswitch_doesnot_contain_subid_entry(self):
"""
This testcase checks that when ipa-client-install
is installed without subid option, the nsswitch.conf
does not contain subid entry or does not use sss as
source for subid
"""
cmd = self.clients[0].run_command(
["grep", "^subid", "/etc/nsswitch.conf"],
raiseonerr=False
)
# a source is defined for the subid database.
# Ensure it is not "sss"
if cmd.returncode == 0:
assert 'sss' not in cmd.stdout_text
else:
# grep command returncode 1 means no matching line
# was found = no source is defined for the subid database,
# which is valid other return codes would
# mean an error occurred
assert cmd.returncode == 1
def test_nsswitch_is_updated_with_subid_entry(self):
"""
This test case checks that when ipa-client-install
is installed with --subid option, the nsswitch.conf
file is modified with the entry 'subid: sss'
"""
tasks.uninstall_client(self.clients[0])
tasks.install_client(self.master, self.clients[0],
extra_args=['--subid'])
cmd = self.clients[0].run_command(
["grep", "^subid", "/etc/nsswitch.conf"]
)
subid = cmd.stdout_text.split()
assert ['subid:', 'sss'] == subid
+40
View File
@@ -101,3 +101,43 @@ class TestSortURI:
assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3]
assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3]
assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2]
class TestDNSResolver:
def test_nameservers(self):
res = dnsutil.DNSResolver()
res.nameservers = ["4.4.4.4", "8.8.8.8"]
assert res.nameservers == ["4.4.4.4", "8.8.8.8"]
def test_nameservers_with_ports(self):
res = dnsutil.DNSResolver()
res.nameservers = ["4.4.4.4 port 53", "8.8.8.8 port 8053"]
assert res.nameservers == ["4.4.4.4", "8.8.8.8"]
assert res.nameserver_ports == {"4.4.4.4": 53, "8.8.8.8": 8053}
res.nameservers = ["4.4.4.4 port 53", "8.8.8.8 port 8053"]
assert res.nameservers == ["4.4.4.4", "8.8.8.8"]
assert res.nameserver_ports == {"4.4.4.4": 53, "8.8.8.8": 8053}
def test_nameservers_with_bad_ports(self):
res = dnsutil.DNSResolver()
try:
res.nameservers = ["4.4.4.4 port a"]
except ValueError:
pass
else:
pytest.fail("No fail on bad port a")
try:
res.nameservers = ["4.4.4.4 port -1"]
except ValueError:
pass
else:
pytest.fail("No fail on bad port -1")
try:
res.nameservers = ["4.4.4.4 port 65536"]
except ValueError:
pass
else:
pytest.fail("No fail on bad port 65536")
@@ -214,8 +214,8 @@ class TestAJPSecretUpgrade:
as mocked_file:
dogtag.secure_ajp_connector()
if rewrite:
newdata = mocked_file().write.call_args.args
f = BytesIO(newdata[0])
newdata = mocked_file().write.call_args
f = BytesIO(newdata[0][0])
server_xml = myparse(f)
doc = server_xml.getroot()
connectors = doc.xpath('//Connector[@protocol="AJP/1.3"]')
+19
View File
@@ -11,6 +11,11 @@ from ipatests.test_webui.ui_driver import screenshot
import pytest
import re
try:
from selenium.webdriver.common.by import By
except ImportError:
pass
@pytest.mark.tier1
class TestMiscCases(UI_driver):
@@ -26,3 +31,17 @@ class TestMiscCases(UI_driver):
ver_re = re.compile('version: .*')
assert re.search(ver_re, about_text), 'Version not found'
self.dialog_button_click('ok')
@screenshot
def test_customization_pagination_input_required(self):
"""Test if 'pagination size' is required when submitting the form."""
self.init_app()
self.profile_menu_action('configuration')
self.fill_input('pagination_size', '')
self.dialog_button_click('save')
pagination_size_elem = self.find(
".widget[name='pagination_size']", By.CSS_SELECTOR)
self.assert_field_validation_required(parent=pagination_size_elem)
@@ -152,6 +152,18 @@ class test_old_permission(Declarative):
expected=dict(
value=permission1,
summary=u'Added permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -493,6 +505,18 @@ class test_old_permission(Declarative):
expected=dict(
value=permission2,
summary=u'Added permission "%s"' % permission2,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission2_dn,
cn=[permission2],
@@ -801,6 +825,18 @@ class test_old_permission(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has read rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'read',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -921,6 +957,18 @@ class test_old_permission(Declarative):
expected=dict(
value=permission1_renamed,
summary=u'Modified permission "%s"' % permission1_renamed,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result={
'dn': permission1_renamed_ucase_dn,
'cn': [permission1_renamed_ucase],
@@ -947,6 +995,18 @@ class test_old_permission(Declarative):
expected=dict(
value=permission1_renamed_ucase,
summary=u'Modified permission "%s"' % permission1_renamed_ucase,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_renamed_ucase_dn,
cn=[permission1_renamed_ucase],
@@ -1175,6 +1235,18 @@ class test_old_permission(Declarative):
expected=dict(
value=permission1,
summary=u'Added permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -1206,6 +1278,18 @@ class test_old_permission(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -1229,6 +1313,18 @@ class test_old_permission(Declarative):
),
expected=dict(
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
value=permission1,
result=dict(
dn=permission1_dn,
@@ -1266,6 +1362,18 @@ class test_old_permission(Declarative):
expected=dict(
value=permission1,
summary=u'Added permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -3167,6 +3167,18 @@ class test_permission_bindtype(Declarative):
expected=dict(
value=permission1,
summary=u'Added permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -3228,6 +3240,18 @@ class test_permission_bindtype(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -3319,6 +3343,18 @@ class test_permission_bindtype(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_renamed_dn,
cn=[permission1_renamed],
@@ -3350,6 +3386,18 @@ class test_permission_bindtype(Declarative):
expected=dict(
value=permission1_renamed,
summary=u'Modified permission "%s"' % permission1_renamed,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_renamed_dn,
cn=[permission1_renamed],
@@ -3379,6 +3427,18 @@ class test_permission_bindtype(Declarative):
expected=dict(
value=permission1_renamed,
summary=u'Modified permission "%s"' % permission1_renamed,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -4010,6 +4070,18 @@ class test_permission_filters(Declarative):
expected=dict(
value=permission1,
summary=u'Added permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -4054,6 +4126,18 @@ class test_permission_filters(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -4093,6 +4177,18 @@ class test_permission_filters(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -4132,6 +4228,18 @@ class test_permission_filters(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -4172,6 +4280,18 @@ class test_permission_filters(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -4205,6 +4325,18 @@ class test_permission_filters(Declarative):
expected=dict(
value=permission1,
summary=u'Modified permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -4254,6 +4386,18 @@ class test_permission_filters(Declarative):
expected=dict(
value=permission1,
summary=u'Added permission "%s"' % permission1,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission1_dn,
cn=[permission1],
@@ -255,6 +255,18 @@ class test_privilege(Declarative):
expected=dict(
value=permission2,
summary=u'Added permission "%s"' % permission2,
messages=(
{
'message': ('The permission has write rights but no '
'attributes are set.'),
'code': 13032,
'type': 'warning',
'name': 'MissingTargetAttributesinPermission',
'data': {
'right': 'write',
}
},
),
result=dict(
dn=permission2_dn,
cn=[permission2],
@@ -479,11 +479,12 @@ class TestActive(XMLRPC_test):
def test_delete_preserve(self, user):
user.ensure_exists()
user.track_delete()
user.track_delete(preserve=True)
command = user.make_delete_command(no_preserve=False, preserve=True)
result = command()
user.check_delete(result)
user.track_delete(preserve=False)
command = user.make_delete_command()
result = command()
user.check_delete(result)
@@ -622,6 +623,7 @@ class TestCustomAttr(XMLRPC_test):
assert 'BusinessCat' in result['result'][u'businesscategory']
# delete the user with --preserve
user_customattr.track_delete(preserve=True)
command = user_customattr.make_delete_command(no_preserve=False,
preserve=True)
result = command()
@@ -763,6 +765,7 @@ class TestGroups(XMLRPC_test):
result = command()
group.check_retrieve(result)
user.track_delete(preserve=True)
command = user.make_delete_command(no_preserve=False, preserve=True)
result = command()
user.check_delete(result)
+14 -6
View File
@@ -125,7 +125,9 @@ def user_npg(request, group):
del tracker.attrs['mepmanagedentry']
tracker.attrs.update(
description=[], memberof_group=[group.cn],
objectclass=objectclasses.user_base + [u'ipantuserattrs'],
objectclass=fuzzy_set_optional_oc(
objectclasses.user_base, 'ipantuserattrs'
),
)
return tracker.make_fixture(request)
@@ -139,7 +141,9 @@ def user_npg2(request, group):
del tracker.attrs['mepmanagedentry']
tracker.attrs.update(
gidnumber=[u'1000'], description=[], memberof_group=[group.cn],
objectclass=objectclasses.user_base + [u'ipantuserattrs'],
objectclass=fuzzy_set_optional_oc(
objectclasses.user_base, 'ipantuserattrs'
),
)
return tracker.make_fixture(request)
@@ -151,8 +155,9 @@ def user_radius(request, xmlrpc_setup):
sn=u'radiususer1',
ipatokenradiususername=u'radiususer')
tracker.track_create()
tracker.attrs.update(
objectclass=objectclasses.user + [u'ipatokenradiusproxyuser']
tracker.attrs.update(objectclass=fuzzy_set_optional_oc(
objectclasses.user + [u'ipatokenradiusproxyuser'],
'ipantuserattrs'),
)
return tracker.make_fixture(request)
@@ -647,7 +652,8 @@ class TestCreate(XMLRPC_test):
testuser.attrs.update(gidnumber=[u'1000'])
testuser.attrs.update(
description=[],
objectclass=objectclasses.user_base + [u'ipantuserattrs']
objectclass=fuzzy_set_optional_oc(
objectclasses.user_base, 'ipantuserattrs'),
)
command = testuser.make_create_command()
result = command()
@@ -865,7 +871,9 @@ class TestUserWithUPGDisabled(XMLRPC_test):
testuser.attrs.update(gidnumber=[u'1000'])
testuser.attrs.update(
description=[],
objectclass=objectclasses.user_base + [u'ipantuserattrs'],
objectclass=fuzzy_set_optional_oc(
objectclasses.user_base, 'ipantuserattrs'
),
)
command = testuser.make_create_command()
result = command()
+6 -1
View File
@@ -273,9 +273,14 @@ class UserTracker(CertmapdataMixin, KerberosAliasMixin, Tracker):
def check_delete(self, result):
""" Check 'user-del' command result """
if u'preserved' in self.attrs and self.attrs[u'preserved']:
summary = u'Preserved user "%s"' % self.uid
else:
summary = u'Deleted user "%s"' % self.uid
assert_deepequal(dict(
value=[self.uid],
summary=u'Deleted user "%s"' % self.uid,
summary=summary,
result=dict(failed=[]),
), result)
+1 -1
View File
@@ -61,7 +61,7 @@ def generate_aci_lines(api):
anonymous_read_aci=None,
is_new=True,
)
aci = perm_plugin.make_aci(entry)
aci, _msg = perm_plugin.make_aci(entry)
yield 'dn: %s\n' % entry.single_value['ipapermlocation']
yield f'aci: {aci}\n'
+1 -1
View File
@@ -62,4 +62,4 @@ exclude=.git,.venv,build,_build,rpmbuild,2_49,2_114,2_156,2_164
filename=*.py,*.in
[pytest]
addopts = -ra -v
addopts = -ra -v --ignore=prci_definitions/prci_checker.py