views: add debug login view (#55557)
This commit is contained in:
parent
dde8fa5d02
commit
dbdd6fd70b
|
@ -0,0 +1,10 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<p><a class="button" href="{% url 'mellon_debug_login' %}">{% trans "Try again" %}</a></p>
|
||||
<p><strong>{% trans "Attributes:" %}</strong> <pre>{{ attributes|pprint }}</pre></p>
|
||||
<p><strong>{% trans "SAML assertion:" %}</strong> <pre>{{ assertion_dump }}</pre></p>
|
||||
<p><strong>{% trans "SAML response:" %}</strong> <pre>{{ response_dump }}</pre></p>
|
||||
<p><strong>{% trans "SAML artifact:" %}</strong> <pre>{{ login.msgBody }}</pre></p>
|
||||
<p><strong>{% trans "Logs:" %}</strong> <pre>{{ logs }}</pre></p>
|
||||
{% endblock %}
|
|
@ -8,6 +8,7 @@ from . import views
|
|||
|
||||
urlpatterns = [
|
||||
url('login/$', views.login, name='mellon_login'),
|
||||
url('login/debug/$', views.debug_login, name='mellon_debug_login'),
|
||||
url('logout/$', views.logout, name='mellon_logout'),
|
||||
url('metadata/$', views.metadata, name='mellon_metadata'),
|
||||
]
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from contextlib import contextmanager, nullcontext
|
||||
from importlib import import_module
|
||||
from io import StringIO
|
||||
import logging
|
||||
import requests
|
||||
import lasso
|
||||
|
@ -26,7 +28,8 @@ import xml.etree.ElementTree as ET
|
|||
|
||||
import django.http
|
||||
from django.views.generic import View
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.views.generic.base import RedirectView
|
||||
from django.http import HttpResponseRedirect, HttpResponse, HttpResponseForbidden
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.conf import settings
|
||||
|
@ -142,6 +145,11 @@ class ProfileMixin(object):
|
|||
|
||||
|
||||
class LoginView(ProfileMixin, LogMixin, View):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.debug_login = request.session.get('mellon_debug_login')
|
||||
with self.capture_logs() if self.debug_login else nullcontext():
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def template_base(self):
|
||||
return self.kwargs.get('template_base', 'base.html')
|
||||
|
@ -290,9 +298,27 @@ class LoginView(ProfileMixin, LogMixin, View):
|
|||
return self.render(request, 'mellon/user_not_found.html', {'saml_attributes': attributes})
|
||||
request.session['lasso_session_dump'] = login.session.dump()
|
||||
|
||||
return HttpResponseRedirect(next_url)
|
||||
if self.debug_login:
|
||||
return self.render_debug_template(request, login, attributes)
|
||||
else:
|
||||
return HttpResponseRedirect(next_url)
|
||||
|
||||
def render_debug_template(self, request, login, attributes):
|
||||
request.session['mellon_debug_login'] = False
|
||||
context = {
|
||||
'logs': self.stream.getvalue(),
|
||||
'attributes': attributes,
|
||||
'login': login,
|
||||
'response_dump': login.response and login.response.debug(4),
|
||||
'assertion_dump': login.assertion and login.assertion.debug(4),
|
||||
}
|
||||
return self.render(request, 'mellon/debug_login.html', context)
|
||||
|
||||
def login(self, user, attributes):
|
||||
if self.debug_login:
|
||||
self.log.info('mellon: would login user %s (username %s)', user.get_full_name(), user)
|
||||
return
|
||||
|
||||
utils.login(self.request, user)
|
||||
session_index = attributes['session_index']
|
||||
if session_index:
|
||||
|
@ -540,6 +566,17 @@ class LoginView(ProfileMixin, LogMixin, View):
|
|||
node.text = hint
|
||||
self.add_extension_node(authn_request, node)
|
||||
|
||||
@contextmanager
|
||||
def capture_logs(self):
|
||||
self.stream = StringIO()
|
||||
handler = logging.StreamHandler(self.stream)
|
||||
handler.setLevel(logging.DEBUG)
|
||||
self.log.root.addHandler(handler)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.log.root.removeHandler(handler)
|
||||
|
||||
|
||||
# we need fine control of transactions to prevent double user creations
|
||||
login = transaction.non_atomic_requests(csrf_exempt(LoginView.as_view()))
|
||||
|
@ -718,3 +755,17 @@ logout = csrf_exempt(LogoutView.as_view())
|
|||
def metadata(request, **kwargs):
|
||||
metadata = utils.create_metadata(request)
|
||||
return HttpResponse(metadata, content_type='text/xml')
|
||||
|
||||
|
||||
class DebugLoginView(RedirectView):
|
||||
pattern_name = 'mellon_login'
|
||||
query_string = True
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not settings.DEBUG:
|
||||
return HttpResponseForbidden()
|
||||
request.session['mellon_debug_login'] = True
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
debug_login = csrf_exempt(DebugLoginView.as_view())
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
from html import unescape
|
||||
import re
|
||||
import base64
|
||||
import zlib
|
||||
|
@ -713,3 +714,21 @@ def test_sso_user_change(db, app, idp, caplog, sp_settings):
|
|||
reverse('mellon_login'), params={'SAMLResponse': other_body, 'RelayState': other_relay_state}
|
||||
)
|
||||
assert 'created new user' in caplog.text
|
||||
|
||||
|
||||
def test_debug_sso(db, app, idp, caplog, sp_settings, settings):
|
||||
response = app.get(reverse('mellon_debug_login') + '?next=/whatever/', status=403)
|
||||
|
||||
settings.DEBUG = True
|
||||
response = app.get(reverse('mellon_debug_login') + '?next=/whatever/')
|
||||
assert urlparse.urlparse(response['Location']).path == '/login/'
|
||||
response = response.follow()
|
||||
url, body, relay_state = idp.process_authn_request_redirect(response['Location'])
|
||||
assert url.endswith(reverse('mellon_login'))
|
||||
response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
|
||||
response_text = unescape(response.text)
|
||||
assert 'Attributes' in response.text
|
||||
assert "'email': ['john.doe@gmail.com']" in response_text
|
||||
assert '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"' in response_text
|
||||
assert '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"' in response_text
|
||||
assert 'mellon: created new user _' in response_text
|
||||
|
|
Loading…
Reference in New Issue