Skip to content

Commit

Permalink
Prettier DRF pages when using trusted proxy (#15579)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
relrod authored Oct 15, 2024
1 parent 1acf8cf commit 6dea7bf
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 4 deletions.
25 changes: 23 additions & 2 deletions awx/api/generics.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
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
Expand All @@ -36,7 +36,7 @@
# 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
Expand Down Expand Up @@ -82,6 +82,12 @@

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
Expand All @@ -97,6 +103,15 @@ def get(self, request, *args, **kwargs):
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:
Expand All @@ -119,6 +134,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)
Expand Down
4 changes: 4 additions & 0 deletions awx/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@
'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'),
Expand Down
20 changes: 18 additions & 2 deletions awx/templates/rest_framework/api.html
Original file line number Diff line number Diff line change
@@ -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 }} &middot; {% trans 'AWX REST API' %}{% endblock %}

{% block bootstrap_theme %}
{% is_proxied_request as proxied %}
<link rel="stylesheet" type="text/css" href="{% static 'rest_framework/css/bootstrap.min.css' %}" />
{% if proxied %}
<style>
{# inline_file from DAB #}
{% inline_file "static/api/api.css" True %}
</style>
{% else %}
<link rel="stylesheet" type="text/css" href="{% static 'api/api.css' %}?v={{tower_version}}" />
{% endif %}
{% endblock %}

{% block style %}
Expand All @@ -24,7 +32,6 @@
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="{% url 'api:api_root_view' %}">
<img class="logo" src="{% static 'media/logo-header.svg' %}">
<span>{% trans 'REST API' %}</span>
</a>
<a class="navbar-title" href="{{ request.get_full_path }}">
Expand Down Expand Up @@ -74,5 +81,14 @@
<a class="toggle-description js-tooltip" href="#" title="Show/Hide Description"><span class="glyphicon glyphicon-question-sign"></span></a>
</div>
{{ block.super }}

{% is_proxied_request as proxied %}
{% if proxied %}
<script>
{# inline_file from DAB #}
{% inline_file "static/api/api.js" True %}
</script>
{% else %}
<script src="{% static 'api/api.js' %}?v={{tower_version}}"></script>
{% endif %}
{% endblock %}

0 comments on commit 6dea7bf

Please sign in to comment.