Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions robottelo/utils/ldap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Utilities for LDAP-related test helpers."""

from robottelo.config import settings


def get_ldap_cacert_pem(sat, server_type, hostname):
"""Fetch the LDAP CA certificate as PEM without installing it in the OS trust store.

:param sat: Satellite host object
:param str server_type: 'IPA' or 'AD'
:param str hostname: LDAP server hostname
:returns: PEM-encoded CA certificate string
"""
if server_type == 'IPA':
result = sat.execute(f'curl -sf http://{hostname}/ipa/config/ca.crt')
assert result.status == 0, f'Failed to fetch IPA CA cert: {result.stderr}'
return result.stdout
if server_type == 'AD':
sat.execute('yum -y --disableplugin=foreman-protector install cifs-utils')
sat.execute(
rf'mount -t cifs -o username=administrator,pass={settings.ldap.password} '
rf'//{hostname}/c\$ /mnt'
)
try:
cert_path = '/mnt/Users/Administrator/Desktop/satqe-QE-SAT6-AD-CA.cer'
result = sat.execute(f'cat {cert_path}')
assert result.status == 0, 'Failed to read AD CA cert'
pem_content = result.stdout
if not pem_content.strip().startswith('-----BEGIN'):
result = sat.execute(f'openssl x509 -inform DER -in {cert_path}')
assert result.status == 0, 'Failed to convert AD CA cert to PEM'
pem_content = result.stdout
finally:
sat.execute('umount /mnt')
return pem_content
raise ValueError(f'Unsupported server_type for CA cert: {server_type}')
46 changes: 46 additions & 0 deletions tests/foreman/cli/test_ldapauthsource.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from robottelo.constants import LDAP_ATTR, LDAP_SERVER_TYPE
from robottelo.exceptions import CLIReturnCodeError
from robottelo.utils.datafactory import generate_strings_list, parametrized
from robottelo.utils.ldap import get_ldap_cacert_pem


@pytest.fixture
Expand Down Expand Up @@ -203,6 +204,51 @@ def test_positive_end_to_end_with_ipa(self, default_ipa_host, server_name, modul
with pytest.raises(CLIReturnCodeError):
module_target_sat.cli.LDAPAuthSource.info({'name': new_name})

@pytest.mark.usefixtures("ldap_tear_down")
def test_positive_create_with_cacert_ipa(self, default_ipa_host, module_target_sat):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for reviewers:
I can't say I'm exactly happy about this test, but I'm afraid we don't really have another way of covering this in an end-to-end fashion.

"""Create LDAP auth source with cacert via hammer CLI and verify it persists.

:id: 5c35665c-05af-426f-8c7c-761c6c6a81c3

:steps:
1. Fetch the IPA CA certificate PEM and write it to a file on the satellite.
2. Create an LDAP auth source with LDAPS and --cacert-file via CLI.
3. Read back with ``hammer auth-source ldap info``.

:expectedresults: The cacert is stored and shown in the CLI info output.
"""
cacert_pem = get_ldap_cacert_pem(module_target_sat, 'IPA', default_ipa_host.hostname)
cacert_file = '/tmp/ipa-ca.crt'
result = module_target_sat.execute(f"cat > {cacert_file} << 'EOF'\n{cacert_pem}\nEOF")
assert result.status == 0, f'Failed to write CA cert to {cacert_file}'
try:
server_name = gen_string('alpha')
auth = module_target_sat.cli_factory.ldap_auth_source(
{
'name': server_name,
'onthefly-register': 'true',
'host': default_ipa_host.hostname,
'server-type': LDAP_SERVER_TYPE['CLI']['ipa'],
'attr-login': LDAP_ATTR['login'],
'attr-firstname': LDAP_ATTR['firstname'],
'attr-lastname': LDAP_ATTR['surname'],
'attr-mail': LDAP_ATTR['mail'],
'account': default_ipa_host.ldap_user_cn,
'account-password': default_ipa_host.ldap_user_passwd,
'base-dn': default_ipa_host.base_dn,
'groups-base': default_ipa_host.group_base_dn,
'tls': '1',
'port': '636',
'cacert-file': cacert_file,
}
)
assert auth['server']['name'] == server_name
auth_info = module_target_sat.cli.LDAPAuthSource.info({'id': auth['server']['id']})
cacert_found = "\n".join(auth_info['server']['ca-certificate'])
assert cacert_found.strip() == cacert_pem.strip()
finally:
module_target_sat.execute(f'rm -f {cacert_file}')

@pytest.mark.parametrize('server_type', ['ipa', 'nonposix_schema'])
@pytest.mark.usefixtures("ldap_tear_down")
def test_usergroup_sync_with_refresh(self, default_ipa_host, server_type, module_target_sat):
Expand Down
76 changes: 36 additions & 40 deletions tests/foreman/destructive/test_ldap_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

"""

import os
from time import sleep

from navmazing import NavigationTriesExceeded
Expand All @@ -21,7 +20,6 @@

from robottelo.config import settings
from robottelo.constants import (
CERT_PATH,
HAMMER_CONFIG,
HAMMER_SESSIONS,
LDAP_ATTR,
Expand All @@ -30,6 +28,7 @@
from robottelo.exceptions import CLIReturnCodeError
from robottelo.logging import logger
from robottelo.utils.datafactory import gen_string
from robottelo.utils.ldap import get_ldap_cacert_pem

pytestmark = [pytest.mark.destructive, pytest.mark.run_in_one_thread]

Expand All @@ -52,36 +51,6 @@
UNABLE_AUTH = 'Unable to authenticate user' # attempting to do something without being logged in


def set_certificate_in_satellite(server_type, sat, hostname=None):
"""update the cert settings in satellite based on type of ldap server"""
if server_type == 'IPA':
certfile = 'ipa.crt'
idm_cert_path_url = os.path.join(settings.ipa.hostname, 'ipa/config/ca.crt')
sat.download_file(file_url=idm_cert_path_url, local_path=CERT_PATH, file_name=certfile)
elif server_type == 'AD':
certfile = 'satqe-QE-SAT6-AD-CA.cer'
assert hostname is not None
sat.execute('yum -y --disableplugin=foreman-protector install cifs-utils')
command = r'mount -t cifs -o username=administrator,pass={0} //{1}/c\$ /mnt'
sat.execute(command.format(settings.ldap.password, hostname))
result = sat.execute(
f'cp /mnt/Users/Administrator/Desktop/satqe-QE-SAT6-AD-CA.cer {CERT_PATH}'
)
if result.status != 0:
raise AssertionError('Failed to copy the AD server certificate at right path')
result = sat.execute(f'update-ca-trust extract && restorecon -R {CERT_PATH}')
if result.status != 0:
raise AssertionError('Failed to update and trust the certificate')
sat.execute(f'install /{CERT_PATH}/{certfile} /etc/pki/tls/certs/')
sat.execute(
f'ln -s {certfile} /etc/pki/tls/certs/$(openssl x509 '
f'-noout -hash -in /etc/pki/tls/certs/{certfile}).0'
)
result = sat.execute('systemctl restart httpd')
if result.status != 0:
raise AssertionError(f'Failed to restart the httpd after applying {server_type} cert')


@pytest.fixture
def ldap_tear_down(module_target_sat):
"""Teardown the all ldap settings user, usergroup and ldap delete"""
Expand Down Expand Up @@ -122,6 +91,20 @@ def rhsso_groups_teardown(module_target_sat, default_sso_host):
default_sso_host.delete_sso_group(group_name)


@pytest.fixture
def untrust_rhit_cas(module_target_sat):
"""Temporarily remove Red Hat IT Root CAs from the system trust store."""
anchors = '/etc/pki/ca-trust/source/anchors'
backup_dir = '/tmp/ca-backup'
module_target_sat.execute(f'mkdir -p {backup_dir}')
module_target_sat.execute(f'mv {anchors}/*-IT-Root-CA.pem {backup_dir}/')
module_target_sat.execute('update-ca-trust extract')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, extract isn't necessary. Just FYI, this is ok.

yield
module_target_sat.execute(f'mv {backup_dir}/* {anchors}/')
module_target_sat.execute(f'rm -rf {backup_dir}')
module_target_sat.execute('update-ca-trust extract')


@pytest.fixture
def configure_hammer_session(parametrized_enrolled_sat, enable=True):
"""Take backup of the hammer config file and enable use_sessions"""
Expand All @@ -143,37 +126,42 @@ def generate_otp(secret):
@pytest.mark.upgrade
@pytest.mark.parametrize('auth_data', ['AD_2019', 'IPA'], indirect=True)
def test_positive_create_with_https(
session, module_subscribe_satellite, test_name, auth_data, ldap_tear_down, module_target_sat
session,
module_subscribe_satellite,
test_name,
auth_data,
ldap_tear_down,
module_target_sat,
untrust_rhit_cas,
):
"""Create LDAP auth_source for IDM with HTTPS.
"""Create LDAP auth_source for IDM/AD with LDAPS.
Comment thread
lhellebr marked this conversation as resolved.

:id: 7ff3daa4-2317-11ea-aeb8-d46d6dd3b5b2

:customerscenario: true

:steps:
1. Create a new LDAP Auth source with HTTPS, provide organization and
1. Create a new LDAP Auth source with LDAPS, provide organization and
location information.
2. Fill in all the fields appropriately.
3. Login with existing LDAP user present.

:BZ: 1785621

:expectedresults: LDAP auth source for HTTPS should be successful and LDAP login
:expectedresults: LDAP auth source for LDAPS should be successful and LDAP login
should work as expected.

:parametrized: yes
"""
if auth_data['auth_type'] == 'ipa':
set_certificate_in_satellite(server_type='IPA', sat=module_target_sat)
username = settings.ipa.user
account_name = auth_data['ldap_user_cn']
server_type = 'IPA'
else:
set_certificate_in_satellite(
server_type='AD', sat=module_target_sat, hostname=auth_data['ldap_hostname']
)
username = settings.ldap.username
account_name = f"cn={auth_data['ldap_user_cn']},{auth_data['base_dn']}"
server_type = 'AD'
cacert_pem = get_ldap_cacert_pem(module_target_sat, server_type, auth_data['ldap_hostname'])
org = module_target_sat.api.Organization().create()
loc = module_target_sat.api.Location().create()
ldap_auth_name = gen_string('alphanumeric')
Expand All @@ -184,6 +172,7 @@ def test_positive_create_with_https(
'ldap_server.name': ldap_auth_name,
'ldap_server.host': auth_data['ldap_hostname'],
'ldap_server.ldaps': True,
'ldap_server.cacert': cacert_pem,
'ldap_server.server_type': auth_data['server_type'],
'account.account_name': account_name,
'account.password': auth_data['ldap_user_passwd'],
Expand All @@ -205,6 +194,13 @@ def test_positive_create_with_https(
assert ldap_source['ldap_server']['name'] == ldap_auth_name
assert ldap_source['ldap_server']['host'] == auth_data['ldap_hostname']
assert ldap_source['ldap_server']['port'] == '636'

# In AD's case, the CA cert uses CRLF line endings
# when this cert is uploaded to Satellite, the line endings are replaced
# with unix LF-only line endings.
assert (
Comment thread
lhellebr marked this conversation as resolved.
ldap_source['ldap_server']['cacert'].strip() == cacert_pem.replace("\r\n", "\n").strip()
)
with (
module_target_sat.ui_session(
test_name, username, auth_data['ldap_user_passwd']
Expand Down
78 changes: 50 additions & 28 deletions tests/foreman/ui/test_ldap_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,47 +12,21 @@

"""

import os

from fauxfactory import gen_url
from navmazing import NavigationTriesExceeded
import pyotp
import pytest

from robottelo.config import settings
from robottelo.constants import ANY_CONTEXT, CERT_PATH, LDAP_ATTR, PERMISSIONS
from robottelo.constants import ANY_CONTEXT, LDAP_ATTR, PERMISSIONS
from robottelo.utils.datafactory import gen_string
from robottelo.utils.ldap import get_ldap_cacert_pem

pytestmark = [pytest.mark.run_in_one_thread]

EXTERNAL_GROUP_NAME = 'foobargroup'


def set_certificate_in_satellite(server_type, target_sat, hostname=None):
"""update the cert settings in satellite based on type of ldap server"""
if server_type == 'IPA':
idm_cert_path_url = os.path.join(settings.ipa.hostname, 'ipa/config/ca.crt')
target_sat.download_file(
file_url=idm_cert_path_url, local_path=CERT_PATH, file_name='ipa.crt'
)
elif server_type == 'AD':
assert hostname is not None
target_sat.execute('yum -y --disableplugin=foreman-protector install cifs-utils')
command = r'mount -t cifs -o username=administrator,pass={0} //{1}/c\$ /mnt'
target_sat.execute(command.format(settings.ldap.password, hostname))
result = target_sat.execute(
f'cp /mnt/Users/Administrator/Desktop/satqe-QE-SAT6-AD-CA.cer {CERT_PATH}'
)
if result.status != 0:
raise AssertionError('Failed to copy the AD server certificate at right path')
result = target_sat.execute(f'update-ca-trust extract && restorecon -R {CERT_PATH}')
if result.status != 0:
raise AssertionError('Failed to update and trust the certificate')
result = target_sat.execute('systemctl restart httpd')
if result.status != 0:
raise AssertionError(f'Failed to restart the httpd after applying {server_type} cert')


@pytest.fixture
def ldap_usergroup_name(target_sat):
"""Return some random usergroup name,
Expand Down Expand Up @@ -1070,6 +1044,54 @@ def test_login_failure_if_internal_user_exist(
target_sat.api.User(id=user.id).delete()


@pytest.mark.parametrize('ldap_auth_source', ['IPA'], indirect=True)
def test_ldaps_cacert_test_connection(session, ldap_auth_source, target_sat):
"""LDAPS auth source fails test connection with a wrong cacert and succeeds with the right one.

:id: eddf4ddf-03d2-4aa1-981f-c30078e3dc54

:steps:
1. Open an existing LDAP auth source created without TLS.
2. Enable LDAPS and provide a wrong cacert (the Satellite host certificate).
3. Click test connection — expect failure.
4. Replace the cacert with the real IPA CA certificate.
5. Click test connection — expect success.

:expectedresults: Test connection fails with a wrong CA certificate and passes once the
correct IPA CA certificate is provided.

:parametrized: yes
"""
ldap_data, auth_source = ldap_auth_source
wrong_cacert_result = target_sat.execute(
'cat /etc/pki/ca-trust/source/anchors/katello_server-host-cert.crt'
)
assert wrong_cacert_result.status == 0, (
f'Failed to read Satellite host cert: {wrong_cacert_result.stderr}'
)
wrong_cacert = wrong_cacert_result.stdout
cacert = get_ldap_cacert_pem(target_sat, 'IPA', ldap_data['ldap_hostname'])
with session:
with pytest.raises(AssertionError) as error:
session.ldapauthentication.test_connection(
{
'ldap_server.host': ldap_data['ldap_hostname'],
'ldap_server.ldaps': True,
'ldap_server.cacert': wrong_cacert,
}
)

assert error.match('certificate verify failed')

session.ldapauthentication.test_connection(
{
'ldap_server.host': ldap_data['ldap_hostname'],
'ldap_server.ldaps': True,
'ldap_server.cacert': cacert,
}
)


def test_userlist_with_external_admin(
session,
auth_source_ipa,
Expand Down