[AAP-53283] Fix analytics API requests to respect proxy environment variables (#16451)

Fix analytics API requests to respect proxy environment variables 
Assisted-by: Claude
This commit is contained in:
Lila Yasin
2026-05-15 13:13:15 -04:00
committed by GitHub
parent 90b7d35554
commit 45480941f8
2 changed files with 116 additions and 25 deletions

View File

@@ -9,7 +9,7 @@ from django.utils import translation
from awx.api.generics import APIView, Response from awx.api.generics import APIView, Response
from awx.api.permissions import AnalyticsPermission from awx.api.permissions import AnalyticsPermission
from awx.api.versioning import reverse from awx.api.versioning import reverse
from awx.main.utils import get_awx_version from awx.main.utils import get_awx_version, set_environ
from awx.main.utils.analytics_proxy import OIDCClient from awx.main.utils.analytics_proxy import OIDCClient
from rest_framework import status from rest_framework import status
@@ -210,31 +210,32 @@ class AnalyticsGenericView(APIView):
return self._error_response(ERROR_UNSUPPORTED_METHOD, method, remote=False, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) return self._error_response(ERROR_UNSUPPORTED_METHOD, method, remote=False, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
url = self._get_analytics_url(request.path) url = self._get_analytics_url(request.path)
using_subscriptions_credentials = False using_subscriptions_credentials = False
try: with set_environ(**settings.AWX_TASK_ENV):
rh_user = getattr(settings, 'REDHAT_USERNAME', None) try:
rh_password = getattr(settings, 'REDHAT_PASSWORD', None) rh_user = getattr(settings, 'REDHAT_USERNAME', None)
if not (rh_user and rh_password): rh_password = getattr(settings, 'REDHAT_PASSWORD', None)
rh_user = self._get_setting('SUBSCRIPTIONS_CLIENT_ID', None, ERROR_MISSING_USER) if not (rh_user and rh_password):
rh_password = self._get_setting('SUBSCRIPTIONS_CLIENT_SECRET', None, ERROR_MISSING_PASSWORD) rh_user = self._get_setting('SUBSCRIPTIONS_CLIENT_ID', None, ERROR_MISSING_USER)
using_subscriptions_credentials = True rh_password = self._get_setting('SUBSCRIPTIONS_CLIENT_SECRET', None, ERROR_MISSING_PASSWORD)
using_subscriptions_credentials = True
client = OIDCClient(rh_user, rh_password) client = OIDCClient(rh_user, rh_password)
response = client.make_request( response = client.make_request(
method, method,
url, url,
headers=headers, headers=headers,
verify=settings.INSIGHTS_CERT_PATH, verify=settings.INSIGHTS_CERT_PATH,
params=getattr(request, 'query_params', {}), params=getattr(request, 'query_params', {}),
json=getattr(request, 'data', {}), json=getattr(request, 'data', {}),
timeout=(31, 31), timeout=(31, 31),
) )
except requests.RequestException: except requests.RequestException:
# subscriptions credentials are not valid for basic auth, so just return 401 # subscriptions credentials are not valid for basic auth, so just return 401
if using_subscriptions_credentials: if using_subscriptions_credentials:
response = Response(status=status.HTTP_401_UNAUTHORIZED) response = Response(status=status.HTTP_401_UNAUTHORIZED)
else: else:
logger.error("Automation Analytics API request failed, trying base auth method") logger.error("Automation Analytics API request failed, trying base auth method")
response = self._base_auth_request(request, method, url, rh_user, rh_password, headers) response = self._base_auth_request(request, method, url, rh_user, rh_password, headers)
# #
# Missing or wrong user/pass # Missing or wrong user/pass
# #

View File

@@ -1,3 +1,4 @@
import os
import pytest import pytest
import requests import requests
from unittest import mock from unittest import mock
@@ -257,3 +258,92 @@ class TestAnalyticsGenericView:
else: else:
# assert mock_base_auth_request not called # assert mock_base_auth_request not called
mock_base_auth_request.assert_not_called() mock_base_auth_request.assert_not_called()
@pytest.mark.django_db
def test__send_to_analytics_respects_proxy_env_oidc(self):
settings_map = {
'INSIGHTS_TRACKING_STATE': True,
'AUTOMATION_ANALYTICS_URL': 'https://example.com',
'REDHAT_USERNAME': 'redhat_user',
'REDHAT_PASSWORD': 'redhat_pass',
'SUBSCRIPTIONS_CLIENT_ID': '',
'SUBSCRIPTIONS_CLIENT_SECRET': '',
'AWX_TASK_ENV': {'HTTPS_PROXY': '192.168.50.100:1234', 'HTTP_PROXY': '192.168.50.100:5678'},
}
with override_settings(**settings_map):
request = RequestFactory().post('/some/path')
view = AnalyticsGenericView()
with mock.patch('awx.api.views.analytics.OIDCClient') as mock_oidc_client:
mock_client_instance = mock.Mock()
mock_oidc_client.return_value = mock_client_instance
def _check_env_and_respond(*args, **kwargs):
assert os.environ.get('HTTPS_PROXY') == '192.168.50.100:1234'
assert os.environ.get('HTTP_PROXY') == '192.168.50.100:5678'
return mock.Mock(status_code=200)
mock_client_instance.make_request.side_effect = _check_env_and_respond
response = view._send_to_analytics(request, 'POST')
assert response.status_code == 200
mock_client_instance.make_request.assert_called_once()
@pytest.mark.django_db
def test__send_to_analytics_respects_proxy_env_basic_auth(self):
settings_map = {
'INSIGHTS_TRACKING_STATE': True,
'AUTOMATION_ANALYTICS_URL': 'https://example.com',
'REDHAT_USERNAME': 'redhat_user',
'REDHAT_PASSWORD': 'redhat_pass',
'SUBSCRIPTIONS_CLIENT_ID': '',
'SUBSCRIPTIONS_CLIENT_SECRET': '',
'AWX_TASK_ENV': {'HTTPS_PROXY': '192.168.50.100:1234'},
}
with override_settings(**settings_map):
request = RequestFactory().post('/some/path')
view = AnalyticsGenericView()
with mock.patch('awx.api.views.analytics.OIDCClient') as mock_oidc_client, mock.patch(
'awx.api.views.analytics.AnalyticsGenericView._base_auth_request'
) as mock_base_auth:
mock_client_instance = mock.Mock()
mock_oidc_client.return_value = mock_client_instance
mock_client_instance.make_request.side_effect = requests.RequestException("OIDC failed")
def _check_env_and_respond(*args, **kwargs):
assert os.environ.get('HTTPS_PROXY') == '192.168.50.100:1234'
return mock.Mock(status_code=200)
mock_base_auth.side_effect = _check_env_and_respond
response = view._send_to_analytics(request, 'POST')
assert response.status_code == 200
mock_base_auth.assert_called_once()
@pytest.mark.django_db
def test__send_to_analytics_restores_env_after_request(self):
original_value = os.environ.pop('HTTPS_PROXY', None)
settings_map = {
'INSIGHTS_TRACKING_STATE': True,
'AUTOMATION_ANALYTICS_URL': 'https://example.com',
'REDHAT_USERNAME': 'redhat_user',
'REDHAT_PASSWORD': 'redhat_pass',
'SUBSCRIPTIONS_CLIENT_ID': '',
'SUBSCRIPTIONS_CLIENT_SECRET': '',
'AWX_TASK_ENV': {'HTTPS_PROXY': '192.168.50.100:1234'},
}
try:
with override_settings(**settings_map):
request = RequestFactory().post('/some/path')
view = AnalyticsGenericView()
with mock.patch('awx.api.views.analytics.OIDCClient') as mock_oidc_client:
mock_client_instance = mock.Mock()
mock_oidc_client.return_value = mock_client_instance
mock_client_instance.make_request.return_value = mock.Mock(status_code=200)
view._send_to_analytics(request, 'POST')
assert 'HTTPS_PROXY' not in os.environ
finally:
if original_value is not None:
os.environ['HTTPS_PROXY'] = original_value