From de293f18d91f7f27aff20fe222d49c239f9de435 Mon Sep 17 00:00:00 2001 From: Satellite QE <115476073+Satellite-QE@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:26:11 -0400 Subject: [PATCH] [6.15.z] Add case for special chars in HTTP proxy password (#14570) --- pytest_fixtures/component/http_proxy.py | 7 ++ robottelo/host_helpers/api_factory.py | 2 +- robottelo/hosts.py | 43 ++++++++++ tests/foreman/ui/test_http_proxy.py | 100 +++++++++++++++++++----- 4 files changed, 133 insertions(+), 19 deletions(-) diff --git a/pytest_fixtures/component/http_proxy.py b/pytest_fixtures/component/http_proxy.py index 8c6095092dd..a98e7409699 100644 --- a/pytest_fixtures/component/http_proxy.py +++ b/pytest_fixtures/component/http_proxy.py @@ -1,6 +1,13 @@ import pytest from robottelo.config import settings +from robottelo.hosts import ProxyHost + + +@pytest.fixture(scope='session') +def session_auth_proxy(session_target_sat): + """Instantiates authenticated HTTP proxy as a session-scoped fixture""" + return ProxyHost(settings.http_proxy.auth_proxy_url) @pytest.fixture diff --git a/robottelo/host_helpers/api_factory.py b/robottelo/host_helpers/api_factory.py index bae286dc8ac..1316bca9078 100644 --- a/robottelo/host_helpers/api_factory.py +++ b/robottelo/host_helpers/api_factory.py @@ -145,7 +145,7 @@ def enable_sync_redhat_repo(self, rh_repo, org_id, timeout=1500): """Enable the RedHat repo, sync it and returns repo_id""" # Enable RH repo and fetch repository_id repo_id = self.enable_rhrepo_and_fetchid( - basearch=rh_repo['basearch'], + basearch=rh_repo.get('basearch', rh_repo.get('arch', DEFAULT_ARCHITECTURE)), org_id=org_id, product=rh_repo['product'], repo=rh_repo['name'], diff --git a/robottelo/hosts.py b/robottelo/hosts.py index 7a250069c95..a5d5e2319f7 100644 --- a/robottelo/hosts.py +++ b/robottelo/hosts.py @@ -171,6 +171,10 @@ class IPAHostError(Exception): pass +class ProxyHostError(Exception): + pass + + class ContentHost(Host, ContentHostMixins): run = Host.execute default_timeout = settings.server.ssh_client.command_timeout @@ -2560,3 +2564,42 @@ def remove_user_from_usergroup(self, member_username, member_group): ) if result.status != 0: raise IPAHostError('Failed to remove the user from user group') + + +class ProxyHost(Host): + """Class representing HTTP Proxy host""" + + def __init__(self, url, **kwargs): + self._conf_dir = '/etc/squid/' + self._access_log = '/var/log/squid/access.log' + kwargs['hostname'] = urlparse(url).hostname + super().__init__(**kwargs) + + def add_user(self, name, passwd): + """Adds new user to the HTTP Proxy""" + res = self.execute(f"htpasswd -b {self._conf_dir}passwd {name} '{passwd}'") + assert res.status == 0, f'User addition failed on the proxy side: {res.stderr}' + return res + + def remove_user(self, name): + """Removes a user from HTTP Proxy""" + res = self.execute(f'htpasswd -D {self._conf_dir}passwd {name}') + assert res.status == 0, f'User deletion failed on the proxy side: {res.stderr}' + return res + + def get_log(self, which=None, tail=None, grep=None): + """Returns log content from the HTTP Proxy instance + + :param which: Which log file should be read. Defaults to access.log. + :param tail: Use when only the tail of a long log file is needed. + :param grep: Grep for some expression. + :return: Log content found or None + """ + log_file = which or self._access_log + cmd = f'tail -n {tail} {log_file}' if tail else f'cat {log_file}' + if grep: + cmd = f'{cmd} | grep "{grep}"' + res = self.execute(cmd) + if res.status != 0: + raise ProxyHostError(f'Proxy log read failed: {res.stderr}') + return None if res.stdout == '' else res.stdout diff --git a/tests/foreman/ui/test_http_proxy.py b/tests/foreman/ui/test_http_proxy.py index 9e3f1e5b10b..a247058f8b6 100644 --- a/tests/foreman/ui/test_http_proxy.py +++ b/tests/foreman/ui/test_http_proxy.py @@ -11,11 +11,23 @@ :CaseAutomation: Automated """ +from box import Box from fauxfactory import gen_integer, gen_string, gen_url import pytest from robottelo.config import settings -from robottelo.constants import DOCKER_REPO_UPSTREAM_NAME, REPO_TYPE +from robottelo.constants import DOCKER_REPO_UPSTREAM_NAME, REPO_TYPE, REPOS +from robottelo.hosts import ProxyHostError + + +@pytest.fixture +def function_spec_char_user(target_sat, session_auth_proxy): + """Creates a user with special character password on the auth HTTP proxy""" + name = gen_string('alpha').lower() # lower! + passwd = gen_string('punctuation').replace("'", '') + session_auth_proxy.add_user(name, passwd) + yield Box(name=name, passwd=passwd) + session_auth_proxy.remove_user(name) @pytest.mark.tier2 @@ -295,35 +307,87 @@ def test_check_http_proxy_value_repository_details( @pytest.mark.tier3 @pytest.mark.run_in_one_thread -@pytest.mark.stubbed -def test_http_proxy_containing_special_characters(): +def test_http_proxy_containing_special_characters( + request, + target_sat, + session_auth_proxy, + function_spec_char_user, + module_sca_manifest_org, + default_location, +): """Test Manifest refresh and redhat repository sync with http proxy special characters in password. :id: 16082c6a-9320-4a9a-bd6c-5687b099c940 - :customerscenario: true + :setup: + 1. Have an authenticated HTTP proxy. + 2. At the Proxy side create a user with special characters in password + (via function_spec_user fixture), let's call him the spec-char user. :steps: - 1. Navigate to Infrastructure > Http Proxies - 2. Create HTTP Proxy with special characters in password. - 3. Go To to Administer > Settings > content tab - 4. Fill the details related to HTTP Proxy and click on "Test connection" button. - 5. Update the "Default HTTP Proxy" with created above. - 6. Refresh manifest. - 7. Enable and sync any redhat repositories. - - :BZ: 1844840 + 1. Check that no logs exist for the spec-char user at the proxy side yet. + 2. Create a proxy via UI using the spec-char user. + 3. Update settings to use the proxy for the content ops. + 4. Refresh the manifest, check it went through the proxy. + 5. Enable and sync some RH repository, check it went through the proxy. :expectedresults: - 1. "Test connection" button workes as expected. - 2. Manifest refresh, repository enable/disable and repository sync operation - finished successfully. + 1. HTTP proxy can be created via UI using the spec-char user. + 2. Manifest refresh, repository enable and sync succeed and are performed + through the HTTP proxy. - :CaseAutomation: NotAutomated + :BZ: 1844840 - :CaseImportance: High + :customerscenario: true """ + # Check that no logs exist for the spec-char user at the proxy side yet. + with pytest.raises(ProxyHostError): + session_auth_proxy.get_log(tail=100, grep=function_spec_char_user.name) + + # Create a proxy via UI using the spec-char user. + proxy_name = gen_string('alpha') + with target_sat.ui_session() as session: + session.organization.select(org_name=module_sca_manifest_org.name) + session.http_proxy.create( + { + 'http_proxy.name': proxy_name, + 'http_proxy.url': settings.http_proxy.auth_proxy_url, + 'http_proxy.username': function_spec_char_user.name, + 'http_proxy.password': function_spec_char_user.passwd, + 'locations.resources.assigned': [default_location.name], + 'organizations.resources.assigned': [module_sca_manifest_org.name], + } + ) + request.addfinalizer( + lambda: target_sat.api.HTTPProxy() + .search(query={'search': f'name={proxy_name}'})[0] + .delete() + ) + + # Update settings to use the proxy for the content ops. + session.settings.update( + 'name = content_default_http_proxy', + f'{proxy_name} ({settings.http_proxy.auth_proxy_url})', + ) + + # Refresh the manifest, check it went through the proxy. + target_sat.cli.Subscription.refresh_manifest( + {'organization-id': module_sca_manifest_org.id} + ) + assert session_auth_proxy.get_log( + tail=100, grep=f'CONNECT subscription.rhsm.redhat.com.*{function_spec_char_user.name}' + ), 'RHSM connection not found in proxy log' + + # Enable and sync some RH repository, check it went through the proxy. + repo_id = target_sat.api_factory.enable_sync_redhat_repo( + REPOS['rhae2'], module_sca_manifest_org.id + ) + repo = target_sat.api.Repository(id=repo_id).read() + assert session_auth_proxy.get_log( + tail=100, grep=f'CONNECT cdn.redhat.com.*{function_spec_char_user.name}' + ), 'CDN connection not found in proxy log' + assert repo.content_counts['rpm'] > 0, 'Where is my content?!' @pytest.mark.tier2