Skip to content

Commit

Permalink
[6.15.z] Add case for special chars in HTTP proxy password (#14570)
Browse files Browse the repository at this point in the history
  • Loading branch information
Satellite-QE authored Apr 2, 2024
1 parent e848a4f commit de293f1
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 19 deletions.
7 changes: 7 additions & 0 deletions pytest_fixtures/component/http_proxy.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion robottelo/host_helpers/api_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down
43 changes: 43 additions & 0 deletions robottelo/hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
100 changes: 82 additions & 18 deletions tests/foreman/ui/test_http_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit de293f1

Please sign in to comment.