From 4820b084c12e848cc4603b55d4eeb9b31f648f8f Mon Sep 17 00:00:00 2001 From: Rick Elrod Date: Wed, 16 Oct 2024 19:58:01 +0200 Subject: [PATCH] Prettier DRF pages when using trusted proxy (#15579) (#6717) This is a rather hacky, but fixes the DRF pages when going through a trusted proxy. Notably: This is meant to primarily fix the DRF pages on downstream builds while leaving the upstream to function as-is. When using a trusted proxy, the DRF login and logout endpoints now redirect to the Platform login page (which respects ?next) and logout endpoint respectively. The CSS and JS is inlined because the trusted proxy might only proxy to /api/ and not /static/ which is a harder problem to solve. Signed-off-by: Rick Elrod --- awx/api/generics.py | 25 +++++++++++++++++++++++-- awx/settings/defaults.py | 4 ++++ awx/templates/rest_framework/api.html | 20 ++++++++++++++++++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/awx/api/generics.py b/awx/api/generics.py index f9b1e04bbc..7e89da397e 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -14,7 +14,7 @@ from django.core.exceptions import FieldDoesNotExist from django.db import connection, transaction from django.db.models.fields.related import OneToOneRel from django.http import QueryDict -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, redirect from django.template.loader import render_to_string from django.utils.encoding import smart_str from django.utils.safestring import mark_safe @@ -33,7 +33,7 @@ from rest_framework.negotiation import DefaultContentNegotiation # django-ansible-base from ansible_base.rest_filters.rest_framework.field_lookup_backend import FieldLookupBackend from ansible_base.lib.utils.models import get_all_field_names -from ansible_base.lib.utils.requests import get_remote_host +from ansible_base.lib.utils.requests import get_remote_host, is_proxied_request from ansible_base.rbac.models import RoleEvaluation, RoleDefinition from ansible_base.rbac.permission_registry import permission_registry from ansible_base.jwt_consumer.common.util import validate_x_trusted_proxy_header @@ -80,6 +80,12 @@ analytics_logger = logging.getLogger('awx.analytics.performance') class LoggedLoginView(auth_views.LoginView): def get(self, request, *args, **kwargs): + if is_proxied_request(): + next = request.GET.get('next', "") + if next: + next = f"?next={next}" + return redirect(f"/{next}") + # The django.auth.contrib login form doesn't perform the content # negotiation we've come to expect from DRF; add in code to catch # situations where Accept != text/html (or */*) and reply with @@ -95,6 +101,15 @@ class LoggedLoginView(auth_views.LoginView): return super(LoggedLoginView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): + if is_proxied_request(): + # Give a message, saying to login via AAP + return Response( + { + 'detail': _('Please log in via Platform Authentication.'), + }, + status=status.HTTP_401_UNAUTHORIZED, + ) + ret = super(LoggedLoginView, self).post(request, *args, **kwargs) ip = get_remote_host(request) # request.META.get('REMOTE_ADDR', None) if request.user.is_authenticated: @@ -117,6 +132,12 @@ class LoggedLogoutView(auth_views.LogoutView): success_url_allowed_hosts = set(settings.LOGOUT_ALLOWED_HOSTS.split(",")) if settings.LOGOUT_ALLOWED_HOSTS else set() def dispatch(self, request, *args, **kwargs): + if is_proxied_request(): + # 1) We intentionally don't obey ?next= here, just always redirect to platform login + # 2) Hack to prevent rewrites of Location header + qs = "?__gateway_no_rewrite__=1&next=/" + return redirect(f"/api/gateway/v1/logout/{qs}") + original_user = getattr(request, 'user', None) ret = super(LoggedLogoutView, self).dispatch(request, *args, **kwargs) current_user = getattr(request, 'user', None) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 396b7e87de..dd6a0bbc16 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -319,6 +319,10 @@ TEMPLATES = [ 'social_django.context_processors.login_redirect', ], 'builtins': ['awx.main.templatetags.swagger'], + 'libraries': { + "ansible_base.lib.templatetags.requests": "ansible_base.lib.templatetags.requests", + "ansible_base.lib.templatetags.util": "ansible_base.lib.templatetags.util", + }, }, 'DIRS': [ os.path.join(BASE_DIR, 'templates'), diff --git a/awx/templates/rest_framework/api.html b/awx/templates/rest_framework/api.html index fbcfe97b30..edfb721efd 100644 --- a/awx/templates/rest_framework/api.html +++ b/awx/templates/rest_framework/api.html @@ -1,11 +1,19 @@ {% extends 'rest_framework/base.html' %} -{% load i18n static %} +{% load i18n static ansible_base.lib.templatetags.requests ansible_base.lib.templatetags.util %} {% block title %}{{ name }} · {% trans 'AWX REST API' %}{% endblock %} {% block bootstrap_theme %} + {% is_proxied_request as proxied %} + {% if proxied %} + + {% else %} + {% endif %} {% endblock %} {% block style %} @@ -24,7 +32,6 @@ - {% trans 'REST API' %} @@ -74,5 +81,14 @@ {{ block.super }} + +{% is_proxied_request as proxied %} +{% if proxied %} + +{% else %} +{% endif %} {% endblock %}