diff --git a/awx/api/views/analytics.py b/awx/api/views/analytics.py index 0c070c186f..097617e6f1 100644 --- a/awx/api/views/analytics.py +++ b/awx/api/views/analytics.py @@ -10,6 +10,7 @@ from awx.api.generics import APIView, Response from awx.api.permissions import AnalyticsPermission from awx.api.versioning import reverse from awx.main.utils import get_awx_version +from awx.main.utils.analytics_proxy import OIDCClient, DEFAULT_OIDC_ENDPOINT from rest_framework import status from collections import OrderedDict @@ -179,32 +180,48 @@ class AnalyticsGenericView(APIView): return Response(response.content, status=response.status_code) + @staticmethod + def _base_auth_request(request: requests.Request, method: str, url: str, user: str, pw: str, headers: dict[str, str]) -> requests.Response: + response = requests.request( + method, + url, + auth=(user, pw), + verify=settings.INSIGHTS_CERT_PATH, + params=getattr(request, 'query_params', {}), + headers=headers, + json=getattr(request, 'data', {}), + timeout=(31, 31), + ) + return response + def _send_to_analytics(self, request, method): try: headers = self._request_headers(request) self._get_setting('INSIGHTS_TRACKING_STATE', False, ERROR_UPLOAD_NOT_ENABLED) + if method not in ["GET", "POST", "OPTIONS"]: + 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) try: rh_user = self._get_setting('REDHAT_USERNAME', None, ERROR_MISSING_USER) rh_password = self._get_setting('REDHAT_PASSWORD', None, ERROR_MISSING_PASSWORD) - except MissingSettings: - rh_user = self._get_setting('SUBSCRIPTIONS_USERNAME', None, ERROR_MISSING_USER) - rh_password = self._get_setting('SUBSCRIPTIONS_PASSWORD', None, ERROR_MISSING_PASSWORD) - - if method not in ["GET", "POST", "OPTIONS"]: - return self._error_response(ERROR_UNSUPPORTED_METHOD, method, remote=False, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) - else: - response = requests.request( + client = OIDCClient(rh_user, rh_password, DEFAULT_OIDC_ENDPOINT, ['api.console']) + response = client.make_request( method, url, - auth=(rh_user, rh_password), + headers=headers, verify=settings.INSIGHTS_CERT_PATH, params=getattr(request, 'query_params', {}), - headers=headers, json=getattr(request, 'data', {}), timeout=(31, 31), ) + except requests.RequestException: + logger.error("Automation Analytics API request failed, trying base auth method") + response = self._base_auth_request(request, method, url, rh_user, rh_password, headers) + except MissingSettings: + rh_user = self._get_setting('SUBSCRIPTIONS_USERNAME', None, ERROR_MISSING_USER) + rh_password = self._get_setting('SUBSCRIPTIONS_PASSWORD', None, ERROR_MISSING_PASSWORD) + response = self._base_auth_request(request, method, url, rh_user, rh_password, headers) # # Missing or wrong user/pass # diff --git a/awx/main/analytics/core.py b/awx/main/analytics/core.py index 9efe27337c..43ee36df33 100644 --- a/awx/main/analytics/core.py +++ b/awx/main/analytics/core.py @@ -22,6 +22,7 @@ from ansible_base.lib.utils.db import advisory_lock from awx.main.models import Job from awx.main.access import access_registry from awx.main.utils import get_awx_http_client_headers, set_environ, datetime_hook +from awx.main.utils.analytics_proxy import OIDCClient, DEFAULT_OIDC_ENDPOINT __all__ = ['register', 'gather', 'ship'] @@ -370,25 +371,31 @@ def ship(path): rh_user = getattr(settings, 'REDHAT_USERNAME', None) rh_password = getattr(settings, 'REDHAT_PASSWORD', None) - if not rh_user or not rh_password: - logger.info('REDHAT_USERNAME and REDHAT_PASSWORD are not set, using SUBSCRIPTIONS_USERNAME and SUBSCRIPTIONS_PASSWORD') - rh_user = getattr(settings, 'SUBSCRIPTIONS_USERNAME', None) - rh_password = getattr(settings, 'SUBSCRIPTIONS_PASSWORD', None) - - if not rh_user: - logger.error('REDHAT_USERNAME and SUBSCRIPTIONS_USERNAME are not set') - return False - if not rh_password: - logger.error('REDHAT_PASSWORD and SUBSCRIPTIONS_USERNAME are not set') - return False - with open(path, 'rb') as f: files = {'file': (os.path.basename(path), f, settings.INSIGHTS_AGENT_MIME)} s = requests.Session() s.headers = get_awx_http_client_headers() s.headers.pop('Content-Type') with set_environ(**settings.AWX_TASK_ENV): - response = s.post(url, files=files, verify=settings.INSIGHTS_CERT_PATH, auth=(rh_user, rh_password), headers=s.headers, timeout=(31, 31)) + if rh_user and rh_password: + try: + client = OIDCClient(rh_user, rh_password, DEFAULT_OIDC_ENDPOINT, ['api.console']) + response = client.make_request("POST", url, headers=s.headers, files=files, verify=settings.INSIGHTS_CERT_PATH, timeout=(31, 31)) + except requests.RequestException: + logger.error("Automation Analytics API request failed, trying base auth method") + response = s.post(url, files=files, verify=settings.INSIGHTS_CERT_PATH, auth=(rh_user, rh_password), headers=s.headers, timeout=(31, 31)) + elif not rh_user or not rh_password: + logger.info('REDHAT_USERNAME and REDHAT_PASSWORD are not set, using SUBSCRIPTIONS_USERNAME and SUBSCRIPTIONS_PASSWORD') + rh_user = getattr(settings, 'SUBSCRIPTIONS_USERNAME', None) + rh_password = getattr(settings, 'SUBSCRIPTIONS_PASSWORD', None) + if rh_user and rh_password: + response = s.post(url, files=files, verify=settings.INSIGHTS_CERT_PATH, auth=(rh_user, rh_password), headers=s.headers, timeout=(31, 31)) + elif not rh_user: + logger.error('REDHAT_USERNAME and SUBSCRIPTIONS_USERNAME are not set') + return False + elif not rh_password: + logger.error('REDHAT_PASSWORD and SUBSCRIPTIONS_USERNAME are not set') + return False # Accept 2XX status_codes if response.status_code >= 300: logger.error('Upload failed with status {}, {}'.format(response.status_code, response.text)) diff --git a/awx/main/utils/analytics_proxy.py b/awx/main/utils/analytics_proxy.py index f46ed7e0ca..cf75d31c02 100644 --- a/awx/main/utils/analytics_proxy.py +++ b/awx/main/utils/analytics_proxy.py @@ -10,6 +10,8 @@ from typing import Optional, Any import requests +DEFAULT_OIDC_ENDPOINT = 'https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token' + class TokenError(requests.RequestException): '''