Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.15.z] Add case for special chars in HTTP proxy password #14570

Merged
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
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
Loading