mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-28 18:01:23 -06:00
17f430efc4
SMTP server may ask or require client's certificate for verification. To support this the underlying Python's functionality is used [0]. Added 3 new options(corresponds to `load_cert_chain`): - smtp_client_cert - the path to a single file in PEM format containing the certificate. - smtp_client_key - the path to a file containing the private key in. - smtp_client_key_pass - the password for decrypting the private key. [0]: https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain Fixes: https://pagure.io/freeipa/issue/8580 Signed-off-by: Stanislav Levin <slev@altlinux.org> Reviewed-By: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
845 lines
30 KiB
Python
845 lines
30 KiB
Python
#
|
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
######
|
|
# This test suite will _expectedly_ fail if run at the end of the UTC day
|
|
# because users would be created during day N and then EPN output checked
|
|
# during day N+1. This is expected and should be ignored as it does not
|
|
# reflect a product bug. -- fcami
|
|
######
|
|
|
|
from __future__ import print_function, absolute_import
|
|
|
|
import base64
|
|
import datetime
|
|
import email
|
|
import json
|
|
import logging
|
|
import os
|
|
import pytest
|
|
import textwrap
|
|
|
|
from subprocess import CalledProcessError
|
|
|
|
from ipaplatform.paths import paths
|
|
from ipatests.test_integration.base import IntegrationTest
|
|
from ipatests.pytest_ipa.integration.firewall import Firewall
|
|
from ipatests.pytest_ipa.integration import tasks
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
EPN_PKG = ["*ipa-client-epn"]
|
|
|
|
SMTP_CLIENT_CERT = os.path.join(paths.OPENSSL_CERTS_DIR, "smtp_client.pem")
|
|
SMTP_CLIENT_KEY = os.path.join(paths.OPENSSL_PRIVATE_DIR, "smtp_client.key")
|
|
SMTP_CLIENT_KEY_PASS = "Secret123"
|
|
|
|
SMTPD_KEY = os.path.join(paths.OPENSSL_PRIVATE_DIR, "postfix.key")
|
|
SMTPD_CERT = os.path.join(paths.OPENSSL_CERTS_DIR, "postfix.pem")
|
|
|
|
DEFAULT_EPN_CONF = textwrap.dedent(
|
|
"""\
|
|
[global]
|
|
"""
|
|
)
|
|
|
|
USER_EPN_CONF = DEFAULT_EPN_CONF + textwrap.dedent(
|
|
"""\
|
|
smtp_user={user}
|
|
smtp_password={password}
|
|
"""
|
|
)
|
|
|
|
STARTTLS_EPN_CONF = USER_EPN_CONF + textwrap.dedent(
|
|
"""\
|
|
smtp_server={server}
|
|
smtp_security=starttls
|
|
"""
|
|
)
|
|
|
|
SSL_EPN_CONF = USER_EPN_CONF + textwrap.dedent(
|
|
"""\
|
|
smtp_server={server}
|
|
smtp_port=465
|
|
smtp_security=ssl
|
|
"""
|
|
)
|
|
|
|
CLIENT_CERT_EPN_CONF = textwrap.dedent(
|
|
"""\
|
|
smtp_client_cert={client_cert}
|
|
smtp_client_key={client_key}
|
|
smtp_client_key_pass={client_key_pass}
|
|
"""
|
|
)
|
|
|
|
def datetime_to_generalized_time(dt):
|
|
"""Convert datetime to LDAP_GENERALIZED_TIME_FORMAT
|
|
Note: Move into ipalib.
|
|
"""
|
|
dt = dt.timetuple()
|
|
generalized_time_str = str(dt.tm_year) + "".join(
|
|
"0" * (2 - len(str(item))) + str(item)
|
|
for item in (dt.tm_mon, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec,)
|
|
)
|
|
return generalized_time_str + "Z"
|
|
|
|
|
|
def postconf(host, option):
|
|
host.run_command(r"postconf -e '%s'" % option)
|
|
|
|
|
|
def configure_postfix(host, realm):
|
|
"""Configure postfix for:
|
|
* SASL auth
|
|
* to be the destination of the IPA domain.
|
|
"""
|
|
# Setup the keytab we need for SASL auth
|
|
host.run_command(r"ipa service-add smtp/%s --force" % host.hostname)
|
|
host.run_command(r"ipa-getkeytab -p smtp/%s -k /etc/postfix/smtp.keytab" %
|
|
host.hostname)
|
|
host.run_command(r"chown root:mail /etc/postfix/smtp.keytab")
|
|
host.run_command(r"chmod 640 /etc/postfix/smtp.keytab")
|
|
|
|
# Configure the SASL smtp service to use GSSAPI
|
|
host.run_command(
|
|
r"sed -i 's/plain login/GSSAPI plain login/' /etc/sasl2/smtpd.conf")
|
|
host.run_command(
|
|
r"sed -i 's/MECH=pam/MECH=kerberos5/' /etc/sysconfig/saslauthd")
|
|
postconf(host,
|
|
'import_environment = MAIL_CONFIG MAIL_DEBUG MAIL_LOGTAG TZ '
|
|
'XAUTHORITY DISPLAY LANG=C KRB5_KTNAME=/etc/postfix/smtp.keytab')
|
|
postconf(host,
|
|
'smtpd_client_restrictions = permit_sasl_authenticated, reject')
|
|
postconf(host,
|
|
'smtpd_recipient_restrictions = permit_sasl_authenticated, reject')
|
|
postconf(host,
|
|
'smtpd_sender_restrictions = permit_sasl_authenticated, reject')
|
|
postconf(host, 'smtpd_sasl_auth_enable = yes')
|
|
postconf(host, 'smtpd_sasl_security_options = noanonymous')
|
|
postconf(host,
|
|
'smtpd_sasl_tls_security_options = $smtpd_sasl_security_options')
|
|
postconf(host, 'broken_sasl_auth_clients = yes')
|
|
postconf(host, 'smtpd_sasl_authenticated_header = yes')
|
|
postconf(host, 'smtpd_sasl_local_domain = %s' % realm)
|
|
# TLS will not be used
|
|
postconf(host, 'smtpd_tls_security_level = none')
|
|
|
|
# disable procmail if exists, make use of default local(8) delivery agent
|
|
postconf(host, "mailbox_command=")
|
|
|
|
# listen on all active interfaces
|
|
postconf(host, "inet_interfaces = all")
|
|
|
|
host.run_command(["systemctl", "restart", "saslauthd"])
|
|
|
|
result = host.run_command(["postconf", "mydestination"])
|
|
mydestination = result.stdout_text.strip() + ", " + host.domain.name
|
|
postconf(host, mydestination)
|
|
|
|
host.run_command(["systemctl", "restart", "postfix"])
|
|
|
|
|
|
def configure_starttls(host):
|
|
"""Obtain a TLS cert for the host and configure postfix for starttls
|
|
|
|
Depends on configure_postfix() being executed first.
|
|
"""
|
|
|
|
host.run_command(["rm", "-f", SMTPD_KEY, SMTPD_CERT])
|
|
host.run_command(["ipa-getcert", "request",
|
|
"-f", SMTPD_CERT,
|
|
"-k", SMTPD_KEY,
|
|
"-K", "smtp/%s" % host.hostname,
|
|
"-D", host.hostname,
|
|
"-O", "postfix",
|
|
"-o", "postfix",
|
|
"-M", "0640",
|
|
"-m", "0640",
|
|
"-w",
|
|
])
|
|
postconf(host, 'smtpd_tls_loglevel = 1')
|
|
postconf(host, 'smtpd_tls_auth_only = yes')
|
|
postconf(host, "smtpd_tls_key_file = {}".format(SMTPD_KEY))
|
|
postconf(host, "smtpd_tls_cert_file = {}".format(SMTPD_CERT))
|
|
postconf(host, 'smtpd_tls_received_header = yes')
|
|
postconf(host, 'smtpd_tls_session_cache_timeout = 3600s')
|
|
# announce STARTTLS support to remote SMTP clients, not require
|
|
postconf(host, 'smtpd_tls_security_level = may')
|
|
|
|
host.run_command(["systemctl", "restart", "postfix"])
|
|
|
|
|
|
def configure_ssl_client_cert(host):
|
|
"""Obtain a TLS cert for the SMTP client and configure postfix for client
|
|
certificate verification.
|
|
|
|
Depends on configure_starttls().
|
|
"""
|
|
host.run_command(["rm", "-f", SMTP_CLIENT_KEY, SMTP_CLIENT_CERT])
|
|
|
|
host.run_command(["ipa-getcert", "request",
|
|
"-f", SMTP_CLIENT_CERT,
|
|
"-k", SMTP_CLIENT_KEY,
|
|
"-K", "smtp_client/%s" % host.hostname,
|
|
"-D", host.hostname,
|
|
"-P", "Secret123",
|
|
"-w",
|
|
])
|
|
|
|
# mandatory TLS encryption
|
|
postconf(host, "smtpd_tls_security_level = encrypt")
|
|
# require a trusted remote SMTP client certificate
|
|
postconf(host, "smtpd_tls_req_ccert = yes")
|
|
# CA certificates of root CAs trusted to sign remote SMTP client cert
|
|
postconf(host, f"smtpd_tls_CAfile = {paths.IPA_CA_CRT}")
|
|
|
|
host.run_command(["systemctl", "restart", "postfix"])
|
|
|
|
|
|
def configure_ssl(host):
|
|
"""Enable the ssl listener on port 465.
|
|
"""
|
|
conf = host.get_file_contents('/etc/postfix/master.cf',
|
|
encoding='utf-8')
|
|
conf += 'smtps inet n - n - - smtpd\n'
|
|
conf += ' -o syslog_name=postfix/smtps\n'
|
|
conf += ' -o smtpd_tls_wrappermode=yes\n'
|
|
conf += ' -o smtpd_sasl_auth_enable=yes\n'
|
|
host.put_file_contents('/etc/postfix/master.cf', conf)
|
|
|
|
host.run_command(["systemctl", "restart", "postfix"])
|
|
|
|
|
|
def decode_header(header):
|
|
"""Decode the header if needed and return the value"""
|
|
# Only support one value for now
|
|
(value, encoding) = email.header.decode_header(header)[0]
|
|
if encoding:
|
|
return value.decode(encoding)
|
|
else:
|
|
return value
|
|
|
|
|
|
def validate_mail(host, id, content):
|
|
"""Retrieve a remote e-mail and determine if it matches the current user"""
|
|
mail = host.get_file_contents('/var/mail/user%d' % id)
|
|
msg = email.message_from_bytes(mail)
|
|
assert decode_header(msg['To']) == 'user%d@%s' % (id, host.domain.name)
|
|
assert decode_header(msg['From']) == 'IPA-EPN <noreply@%s>' % \
|
|
host.domain.name
|
|
assert decode_header(msg['subject']) == 'Your password will expire soon.'
|
|
|
|
for part in msg.walk():
|
|
if part.get_content_maintype() == 'multipart':
|
|
continue
|
|
body = part.get_payload()
|
|
decoded = base64.b64decode(body).decode('utf-8')
|
|
assert content in decoded
|
|
|
|
|
|
class TestEPN(IntegrationTest):
|
|
"""Test Suite for EPN: https://pagure.io/freeipa/issue/3687
|
|
"""
|
|
|
|
num_clients = 1
|
|
notify_ttls = (28, 14, 7, 3, 1)
|
|
|
|
def _check_epn_output(
|
|
self,
|
|
host,
|
|
dry_run=False,
|
|
mailtest=False,
|
|
from_nbdays=None,
|
|
to_nbdays=None,
|
|
raiseonerr=True,
|
|
validatejson=True
|
|
):
|
|
result = tasks.ipa_epn(
|
|
host,
|
|
from_nbdays=from_nbdays,
|
|
to_nbdays=to_nbdays,
|
|
mailtest=mailtest,
|
|
dry_run=dry_run,
|
|
raiseonerr=raiseonerr
|
|
)
|
|
if validatejson:
|
|
json.dumps(json.loads(result.stdout_text), ensure_ascii=False)
|
|
return (result.stdout_text, result.stderr_text, result.returncode)
|
|
|
|
@classmethod
|
|
def install(cls, mh):
|
|
# External DNS is only available before install so cache a copy
|
|
# of the *ipa-epn-client package so we can experimentally remove
|
|
# it later.
|
|
#
|
|
# Notes:
|
|
# - A package can't be downloaded that is already installed so we
|
|
# have to remove it first.
|
|
# - dnf cleans up previously downloaded locations so make a copy it
|
|
# doesn't know about.
|
|
# - Adds a class variable, pkg, containing the package name of
|
|
# the downloaded *ipa-client-epn rpm.
|
|
hosts = [cls.master, cls.clients[0]]
|
|
tasks.uninstall_packages(cls.clients[0],EPN_PKG)
|
|
pkgdir = tasks.download_packages(cls.clients[0], EPN_PKG)
|
|
pkg = cls.clients[0].run_command(r'ls -1 {}'.format(pkgdir))
|
|
cls.pkg = pkg.stdout_text.strip()
|
|
cls.clients[0].run_command(['cp',
|
|
os.path.join(pkgdir, cls.pkg),
|
|
'/tmp'])
|
|
cls.clients[0].run_command(r'rm -rf {}'.format(pkgdir))
|
|
|
|
for host in hosts:
|
|
tasks.install_packages(host, EPN_PKG + ["postfix"])
|
|
try:
|
|
tasks.install_packages(host, ["cyrus-sasl"])
|
|
except Exception:
|
|
# the package is likely already installed
|
|
pass
|
|
|
|
tasks.install_master(cls.master, setup_dns=True)
|
|
tasks.install_client(cls.master, cls.clients[0])
|
|
for host in hosts:
|
|
configure_postfix(host, cls.master.domain.realm)
|
|
Firewall(host).enable_services(["smtp", "smtps"])
|
|
|
|
|
|
@classmethod
|
|
def uninstall(cls, mh):
|
|
super(TestEPN, cls).uninstall(mh)
|
|
tasks.uninstall_packages(cls.master,EPN_PKG)
|
|
tasks.uninstall_packages(cls.master, ["postfix"])
|
|
tasks.uninstall_packages(cls.clients[0], EPN_PKG)
|
|
tasks.uninstall_packages(cls.clients[0], ["postfix"])
|
|
cls.master.run_command(r'rm -f /etc/postfix/smtp.keytab')
|
|
|
|
for cert in [SMTPD_CERT, SMTP_CLIENT_CERT]:
|
|
cls.master.run_command(["getcert", "stop-tracking", "-f", cert])
|
|
|
|
cls.master.run_command(
|
|
[
|
|
"rm",
|
|
"-f",
|
|
SMTPD_CERT,
|
|
SMTPD_KEY,
|
|
SMTP_CLIENT_CERT,
|
|
SMTP_CLIENT_KEY,
|
|
]
|
|
)
|
|
|
|
@pytest.mark.skip_if_platform(
|
|
"debian", reason="Cannot check installed packages using RPM"
|
|
)
|
|
def test_EPN_config_file(self):
|
|
"""Check that the EPN configuration file is installed.
|
|
https://pagure.io/freeipa/issue/8374
|
|
"""
|
|
epn_conf = "/etc/ipa/epn.conf"
|
|
epn_template = "/etc/ipa/epn/expire_msg.template"
|
|
if tasks.get_platform(self.master) != "fedora":
|
|
cmd1 = self.master.run_command(["rpm", "-qc", "ipa-client-epn"])
|
|
else:
|
|
cmd1 = self.master.run_command(["rpm", "-qc", "freeipa-client-epn"])
|
|
assert epn_conf in cmd1.stdout_text
|
|
assert epn_template in cmd1.stdout_text
|
|
cmd2 = self.master.run_command(["sha256sum", epn_conf])
|
|
ck = "9977d846539d4945900bd04bae25bf746ac75fb561d3769014002db04e1790b8"
|
|
assert cmd2.stdout_text.find(ck) == 0
|
|
|
|
def test_EPN_connection_refused(self):
|
|
"""Test EPN behavior when the configured SMTP is down
|
|
"""
|
|
|
|
self.master.run_command(["systemctl", "stop", "postfix"])
|
|
(unused, stderr_text, rc) = self._check_epn_output(
|
|
self.master, mailtest=True,
|
|
raiseonerr=False, validatejson=False
|
|
)
|
|
self.master.run_command(["systemctl", "start", "postfix"])
|
|
assert "IPA-EPN: Could not connect to the configured SMTP server" in \
|
|
stderr_text
|
|
assert rc > 0
|
|
|
|
def test_EPN_no_security_downgrade_starttls(self):
|
|
"""Configure postfix without starttls and test no auth happens
|
|
"""
|
|
epn_conf = STARTTLS_EPN_CONF.format(
|
|
server=self.master.hostname,
|
|
user=self.master.config.admin_name,
|
|
password=self.master.config.admin_password,
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
|
|
(unused, stderr_text, rc) = self._check_epn_output(
|
|
self.master, mailtest=True,
|
|
raiseonerr=False, validatejson=False
|
|
)
|
|
expected_msg = "IPA-EPN: Unable to create an encrypted session to"
|
|
assert expected_msg in stderr_text
|
|
assert rc > 0
|
|
|
|
def test_EPN_no_security_downgrade_tls(self):
|
|
"""Configure postfix without tls and test no auth happens
|
|
"""
|
|
epn_conf = SSL_EPN_CONF.format(
|
|
server=self.master.hostname,
|
|
user=self.master.config.admin_name,
|
|
password=self.master.config.admin_password,
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
|
|
(unused, stderr_text, rc) = self._check_epn_output(
|
|
self.master, mailtest=True,
|
|
raiseonerr=False, validatejson=False
|
|
)
|
|
expected_msg = (
|
|
"IPA-EPN: Could not connect to the configured SMTP "
|
|
"server"
|
|
)
|
|
assert expected_msg in stderr_text
|
|
assert rc > 0
|
|
|
|
def test_EPN_smoketest_1(self):
|
|
"""No users except admin. Check --dry-run output.
|
|
With the default configuration, the result should be an empty list.
|
|
Also check behavior on master and client alike.
|
|
"""
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', DEFAULT_EPN_CONF)
|
|
# check EPN on client (LDAP+GSSAPI)
|
|
(stdout_text, unused, _unused) = self._check_epn_output(
|
|
self.clients[0], dry_run=True
|
|
)
|
|
assert len(json.loads(stdout_text)) == 0
|
|
# check EPN on master (LDAPI)
|
|
(stdout_text, unused, _unused) = self._check_epn_output(
|
|
self.master, dry_run=True
|
|
)
|
|
assert len(json.loads(stdout_text)) == 0
|
|
|
|
@pytest.fixture
|
|
def cleanupusers(self):
|
|
"""Fixture to remove any users added as part of the tests.
|
|
|
|
It isn't necessary to remove all users created.
|
|
|
|
Ignore all errors.
|
|
"""
|
|
yield
|
|
for user in ["testuser0", "testuser1"]:
|
|
try:
|
|
self.master.run_command(['ipa', 'user-del', user])
|
|
except Exception:
|
|
pass
|
|
|
|
@pytest.fixture
|
|
def cleanupmail(self):
|
|
"""Cleanup any existing mail that has been sent."""
|
|
cmd = ["rm", "-f"]
|
|
for i in range(30):
|
|
cmd.append("/var/mail/user%d" % i)
|
|
self.master.run_command(cmd)
|
|
|
|
def test_EPN_smoketest_2(self, cleanupusers):
|
|
"""Add a user without password.
|
|
Add a user whose password expires within the default time range.
|
|
Check --dry-run output.
|
|
"""
|
|
tasks.user_add(self.master, "testuser0")
|
|
tasks.user_add(
|
|
self.master,
|
|
"testuser1",
|
|
password="Secret123",
|
|
extra_args=[
|
|
"--password-expiration",
|
|
datetime_to_generalized_time(
|
|
datetime.datetime.utcnow() + datetime.timedelta(days=7)
|
|
),
|
|
],
|
|
)
|
|
(stdout_text_client, unused, _unused) = self._check_epn_output(
|
|
self.clients[0], dry_run=True
|
|
)
|
|
(stdout_text_master, unused, _unused) = self._check_epn_output(
|
|
self.master, dry_run=True
|
|
)
|
|
assert stdout_text_master == stdout_text_client
|
|
assert "testuser0" not in stdout_text_client
|
|
assert "testuser1" in stdout_text_client
|
|
|
|
def test_EPN_smoketest_3(self):
|
|
"""Add a bunch of users with incrementally expiring passwords
|
|
(one per day). Check --dry-run output.
|
|
"""
|
|
|
|
users = {}
|
|
userbase_str = "user"
|
|
|
|
for i in range(30):
|
|
uid = userbase_str + str(i)
|
|
users[i] = dict(
|
|
uid=uid,
|
|
days=i,
|
|
krbpasswordexpiration=datetime_to_generalized_time(
|
|
datetime.datetime.utcnow() + datetime.timedelta(days=i)
|
|
),
|
|
)
|
|
|
|
for key in users:
|
|
tasks.user_add(
|
|
self.master,
|
|
users[key]["uid"],
|
|
extra_args=[
|
|
"--password-expiration",
|
|
users[key]["krbpasswordexpiration"],
|
|
],
|
|
password=None,
|
|
)
|
|
|
|
(stdout_text_client, unused, _unused) = self._check_epn_output(
|
|
self.clients[0], dry_run=True
|
|
)
|
|
(stdout_text_master, unused, _unused) = self._check_epn_output(
|
|
self.master, dry_run=True
|
|
)
|
|
assert stdout_text_master == stdout_text_client
|
|
user_lst = []
|
|
for user in json.loads(stdout_text_master):
|
|
user_lst.append(user["uid"])
|
|
expected_users = ["user1", "user3", "user7", "user14", "user28"]
|
|
assert sorted(user_lst) == sorted(expected_users)
|
|
|
|
def test_EPN_nbdays_0(self, cleanupmail):
|
|
"""Test the to/from nbdays options (implies --dry-run)
|
|
|
|
We have a set of users installed with varying expiration
|
|
dates. Confirm that to/from nbdays finds them.
|
|
|
|
Make sure --dry-run does not accidentally send emails.
|
|
"""
|
|
|
|
# Use the notify_ttls values with a 1-day sliding window
|
|
for i in self.notify_ttls:
|
|
user_list = []
|
|
(stdout_text_client, unused, _unused) = self._check_epn_output(
|
|
self.clients[0], from_nbdays=i, to_nbdays=i + 1, dry_run=True
|
|
)
|
|
for user in json.loads(stdout_text_client):
|
|
user_list.append(user["uid"])
|
|
assert len(user_list) == 1
|
|
userid = "user{id}".format(id=i)
|
|
assert user_list[0] == userid
|
|
|
|
# Check that the user list is expected for any given notify_ttls.
|
|
(stdout_text_client, unused, _unused) = self._check_epn_output(
|
|
self.clients[0], to_nbdays=i
|
|
)
|
|
user_list = [user["uid"] for user in json.loads(stdout_text_client)]
|
|
assert len(user_list) == 1
|
|
assert user_list[0] == "user{id}".format(id=i - 1)
|
|
|
|
# make sure no emails were sent
|
|
result = self.clients[0].run_command(['ls', '-lha', '/var/mail/'])
|
|
assert userid not in result.stdout_text
|
|
|
|
def test_EPN_nbdays_1(self, cleanupmail):
|
|
"""Test that for a given range, we find the users in that range"""
|
|
|
|
# Use hardcoded date ranges for now
|
|
for date_range in [(0, 5), (7, 15), (1, 20)]:
|
|
expected_user_list = ["user{i}".format(i=i)
|
|
for i in range(date_range[0], date_range[1])]
|
|
(stdout_text_client, unused, _unused) = self._check_epn_output(
|
|
self.clients[0],
|
|
from_nbdays=date_range[0],
|
|
to_nbdays=date_range[1]
|
|
)
|
|
user_list = [user["uid"] for user in json.loads(stdout_text_client)]
|
|
for user in expected_user_list:
|
|
assert user in user_list
|
|
for user in user_list:
|
|
assert user in expected_user_list
|
|
|
|
# Test the to/from nbdays options behavior with illegal input
|
|
|
|
def test_EPN_nbdays_input_0(self):
|
|
"""Make sure that --to-nbdays implies --dry-run ;
|
|
therefore check that the output is valid JSON and contains the
|
|
expected user.
|
|
"""
|
|
|
|
(stdout_text_client, unused, _unused) = self._check_epn_output(
|
|
self.clients[0], to_nbdays=5, dry_run=False
|
|
)
|
|
assert len(json.loads(stdout_text_client)) == 1
|
|
assert json.loads(stdout_text_client)[0]["uid"] == "user4"
|
|
|
|
def test_EPN_nbdays_input_1(self):
|
|
"""Make sure that --from-nbdays cannot be used without --to-nbdays"""
|
|
|
|
(unused, stderr_text_client, rc) = \
|
|
self._check_epn_output(
|
|
self.clients[0], from_nbdays=3,
|
|
raiseonerr=False, validatejson=False
|
|
)
|
|
assert "You cannot specify --from-nbdays without --to-nbdays" \
|
|
in stderr_text_client
|
|
assert rc > 0
|
|
|
|
def test_EPN_nbdays_input_2(self):
|
|
"""alpha input"""
|
|
|
|
(unused, stderr, rc) = self._check_epn_output(
|
|
self.clients[0], to_nbdays="abc",
|
|
raiseonerr=False, validatejson=False
|
|
)
|
|
assert "error: --to-nbdays must be a positive integer." in stderr
|
|
assert rc > 0
|
|
|
|
def test_EPN_nbdays_input_3(self):
|
|
"""from_nbdays > to_nbdays"""
|
|
|
|
(unused, stderr, rc) = self._check_epn_output(
|
|
self.clients[0], from_nbdays=9, to_nbdays=7,
|
|
raiseonerr=False, validatejson=False
|
|
)
|
|
assert "error: --from-nbdays must be smaller than --to-nbdays." in \
|
|
stderr
|
|
assert rc > 0
|
|
|
|
def test_EPN_nbdays_input_4(self):
|
|
"""decimal input"""
|
|
|
|
(unused, stderr, rc) = self._check_epn_output(
|
|
self.clients[0], to_nbdays=7.3,
|
|
raiseonerr=False, validatejson=False
|
|
)
|
|
logger.info(stderr)
|
|
assert rc > 0
|
|
assert "error: --to-nbdays must be a positive integer." in stderr
|
|
|
|
# From here the tests build on one another:
|
|
# 1) add auth
|
|
# 2) tweak the template
|
|
# 3) add starttls
|
|
|
|
def test_EPN_authenticated(self, cleanupmail):
|
|
"""Enable authentication and test that mail is delivered
|
|
"""
|
|
epn_conf = USER_EPN_CONF.format(
|
|
user=self.master.config.admin_name,
|
|
password=self.master.config.admin_password,
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
|
|
tasks.ipa_epn(self.master)
|
|
for i in self.notify_ttls:
|
|
validate_mail(self.master, i,
|
|
"Hi test user,\n\nYour password will expire")
|
|
|
|
def test_EPN_template(self, cleanupmail):
|
|
"""Modify the template to ensure changes are applied.
|
|
"""
|
|
exp_msg = textwrap.dedent('''
|
|
Hi {{ first }} {{last}},
|
|
Your login entry {{uid}} is going to expire on
|
|
{{ expiration }}. Please change it soon.
|
|
|
|
Your friendly neighborhood admins.
|
|
''')
|
|
self.master.put_file_contents('/etc/ipa/epn/expire_msg.template',
|
|
exp_msg)
|
|
|
|
tasks.ipa_epn(self.master)
|
|
for i in self.notify_ttls:
|
|
validate_mail(self.master, i,
|
|
"Hi test user,\nYour login entry user%d is going" % i)
|
|
|
|
def test_mailtest(self, cleanupmail):
|
|
"""Execute mailtest to validate mail is working
|
|
|
|
Set of of our pre-created users as the smtp_admin to receive
|
|
the mail, run ipa-epn --mailtest, then validate the result.
|
|
|
|
Using a non-expired user here, user2, to receive the result.
|
|
"""
|
|
epn_conf = (
|
|
USER_EPN_CONF
|
|
+ textwrap.dedent(
|
|
"""\
|
|
smtp_admin=user2@{domain}
|
|
"""
|
|
)
|
|
).format(
|
|
user=self.master.config.admin_name,
|
|
password=self.master.config.admin_password,
|
|
domain=self.master.domain.name,
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
|
|
tasks.ipa_epn(self.master, mailtest=True)
|
|
validate_mail(self.master, 2,
|
|
"Hi SAMPLE USER,\nYour login entry SAUSER is going")
|
|
|
|
def test_mailtest_dry_run(self):
|
|
try:
|
|
tasks.ipa_epn(self.master, mailtest=True, dry_run=True)
|
|
except CalledProcessError as e:
|
|
assert 'You cannot specify' in e.stderr
|
|
else:
|
|
raise AssertionError('--mail-test and --dry-run aren\'t supposed '
|
|
'to succeed')
|
|
|
|
def test_EPN_starttls(self, cleanupmail):
|
|
"""Configure with starttls and test delivery
|
|
"""
|
|
epn_conf = STARTTLS_EPN_CONF.format(
|
|
server=self.master.hostname,
|
|
user=self.master.config.admin_name,
|
|
password=self.master.config.admin_password,
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
configure_starttls(self.master)
|
|
|
|
tasks.ipa_epn(self.master)
|
|
for i in self.notify_ttls:
|
|
validate_mail(self.master, i,
|
|
"Hi test user,\nYour login entry user%d is going" % i)
|
|
|
|
def test_EPN_ssl(self, cleanupmail):
|
|
"""Configure with ssl and test delivery
|
|
"""
|
|
epn_conf = SSL_EPN_CONF.format(
|
|
server=self.master.hostname,
|
|
user=self.master.config.admin_name,
|
|
password=self.master.config.admin_password,
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
configure_ssl(self.master)
|
|
|
|
tasks.ipa_epn(self.master)
|
|
for i in self.notify_ttls:
|
|
validate_mail(self.master, i,
|
|
"Hi test user,\nYour login entry user%d is going" % i)
|
|
|
|
def test_EPN_ssl_client_cert(self, cleanupmail):
|
|
"""Configure with ssl + client certificate and test delivery
|
|
"""
|
|
epn_conf = (SSL_EPN_CONF + CLIENT_CERT_EPN_CONF).format(
|
|
server=self.master.hostname,
|
|
user=self.master.config.admin_name,
|
|
password=self.master.config.admin_password,
|
|
client_cert=SMTP_CLIENT_CERT,
|
|
client_key=SMTP_CLIENT_KEY,
|
|
client_key_pass=SMTP_CLIENT_KEY_PASS,
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
configure_ssl_client_cert(self.master)
|
|
|
|
tasks.ipa_epn(self.master)
|
|
for i in self.notify_ttls:
|
|
validate_mail(
|
|
self.master,
|
|
i,
|
|
"Hi test user,\nYour login entry user%d is going" % i
|
|
)
|
|
|
|
def test_EPN_starttls_client_cert(self, cleanupmail):
|
|
"""Configure with starttls + client certificate and test delivery
|
|
"""
|
|
epn_conf = (STARTTLS_EPN_CONF + CLIENT_CERT_EPN_CONF).format(
|
|
server=self.master.hostname,
|
|
user=self.master.config.admin_name,
|
|
password=self.master.config.admin_password,
|
|
client_cert=SMTP_CLIENT_CERT,
|
|
client_key=SMTP_CLIENT_KEY,
|
|
client_key_pass=SMTP_CLIENT_KEY_PASS,
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
|
|
tasks.ipa_epn(self.master)
|
|
for i in self.notify_ttls:
|
|
validate_mail(
|
|
self.master,
|
|
i,
|
|
"Hi test user,\nYour login entry user%d is going" % i
|
|
)
|
|
|
|
def test_EPN_delay_config(self, cleanupmail):
|
|
"""Test the smtp_delay configuration option
|
|
"""
|
|
epn_conf = DEFAULT_EPN_CONF + textwrap.dedent(
|
|
"""\
|
|
smtp_delay=A
|
|
"""
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
|
|
result = tasks.ipa_epn(self.master, raiseonerr=False)
|
|
assert "could not convert string to float: 'A'" in result.stderr_text
|
|
|
|
epn_conf = DEFAULT_EPN_CONF + textwrap.dedent(
|
|
"""\
|
|
smtp_delay=-1
|
|
"""
|
|
)
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', epn_conf)
|
|
result = tasks.ipa_epn(self.master, raiseonerr=False)
|
|
assert "smtp_delay cannot be less than zero" in result.stderr_text
|
|
|
|
def test_EPN_admin(self):
|
|
"""The admin user is special and has no givenName by default
|
|
It also doesn't by default have an e-mail address
|
|
Check --dry-run output.
|
|
"""
|
|
self.master.put_file_contents('/etc/ipa/epn.conf', DEFAULT_EPN_CONF)
|
|
self.master.run_command(
|
|
['ipa', 'user-mod', 'admin', '--password-expiration',
|
|
datetime_to_generalized_time(
|
|
datetime.datetime.utcnow() + datetime.timedelta(days=7)
|
|
)]
|
|
)
|
|
(unused, stderr_text, _unused) = self._check_epn_output(
|
|
self.master, dry_run=True
|
|
)
|
|
assert "uid=admin" in stderr_text
|
|
|
|
@pytest.mark.skip_if_platform(
|
|
"debian", reason="Don't know how to download-only pkgs in Debian"
|
|
)
|
|
def test_EPN_reinstall(self):
|
|
"""Test that EPN can be installed, uninstalled and reinstalled.
|
|
|
|
Since post-install we no longer have access to the repos
|
|
the package is downloaded and stored prior to server
|
|
installation.
|
|
"""
|
|
tasks.uninstall_packages(self.clients[0], EPN_PKG)
|
|
tasks.install_packages(self.clients[0],
|
|
[os.path.join('/tmp', self.pkg)])
|
|
self.clients[0].run_command(r'rm -f /tmp/{}'.format(self.pkg))
|
|
|
|
# re-installing will create a new epn.conf so any execution
|
|
# of ipa-epn will verify the reinstall was ok. Since the previous
|
|
# test would have failed this one should be ok with new config.
|
|
|
|
# Re-run the admin user expected failure
|
|
(unused, stderr_text, _unused) = self._check_epn_output(
|
|
self.master, dry_run=True
|
|
)
|
|
assert "uid=admin" in stderr_text
|