From 6cc4cf4155fb2cc3f99f2ed1508bc7ac88cf3b4f Mon Sep 17 00:00:00 2001
From: Valentin Deniaud {% trans "Attributes:" %} {{ attributes|pprint }}
{% trans "SAML assertion:" %}
{{ assertion_dump }}+
{% trans "SAML response:" %}
{{ response_dump }}+
{% trans "SAML artifact:" %}
{{ login.msgBody }}+
{% trans "Logs:" %}
{{ logs }}+{% endblock %} diff --git a/mellon/urls.py b/mellon/urls.py index 1660264..dca25b5 100644 --- a/mellon/urls.py +++ b/mellon/urls.py @@ -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'), ] diff --git a/mellon/views.py b/mellon/views.py index dcea735..34e7a8b 100644 --- a/mellon/views.py +++ b/mellon/views.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals from importlib import import_module +from io import StringIO import logging import requests import lasso @@ -26,7 +27,7 @@ import xml.etree.ElementTree as ET import django.http from django.views.generic import View -from django.http import HttpResponseRedirect, HttpResponse +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 +143,17 @@ class ProfileMixin(object): class LoginView(ProfileMixin, LogMixin, View): + allow_redirect_to_debug = True + + def dispatch(self, request, *args, **kwargs): + if request.session.get('mellon_debug_login') and self.allow_redirect_to_debug: + url = reverse('mellon_debug_login') + url = "%s?%s" % (url, request.META.get('QUERY_STRING', '')) + if request.method == 'POST': + url += '&' + request.POST.urlencode() + return HttpResponseRedirect(url) + return super().dispatch(request, *args, **kwargs) + @property def template_base(self): return self.kwargs.get('template_base', 'base.html') @@ -277,24 +289,7 @@ class LoginView(ProfileMixin, LogMixin, View): next_url = self.get_next_url(default=resolve_url(settings.LOGIN_REDIRECT_URL)) if user is not None: if user.is_active: - utils.login(request, user) - session_index = attributes['session_index'] - if session_index: - if not request.session.session_key: - request.session.create() - models.SessionIndex.objects.get_or_create( - saml_identifier=user.saml_identifier, - session_key=request.session.session_key, - session_index=session_index, - ) - self.log.info( - 'user %s (NameID is %r) logged in using SAML', user, attributes['name_id_content'] - ) - request.session['mellon_session'] = utils.flatten_datetime(attributes) - if 'session_not_on_or_after' in attributes and not settings.SESSION_EXPIRE_AT_BROWSER_CLOSE: - request.session.set_expiry( - utils.get_seconds_expiry(attributes['session_not_on_or_after']) - ) + self.login(user, attributes) else: self.log.warning( 'user %s (NameID is %r) is inactive, login refused', user, attributes['name_id_content'] @@ -309,6 +304,26 @@ class LoginView(ProfileMixin, LogMixin, View): return HttpResponseRedirect(next_url) + def login(self, user, attributes): + utils.login(self.request, user) + session_index = attributes['session_index'] + if session_index: + if not self.request.session.session_key: + self.request.session.create() + models.SessionIndex.objects.get_or_create( + saml_identifier=user.saml_identifier, + session_key=self.request.session.session_key, + session_index=session_index, + ) + self.log.info( + 'user %s (NameID is %r) logged in using SAML', user, attributes['name_id_content'] + ) + self.request.session['mellon_session'] = utils.flatten_datetime(attributes) + if 'session_not_on_or_after' in attributes and not settings.SESSION_EXPIRE_AT_BROWSER_CLOSE: + self.request.session.set_expiry( + utils.get_seconds_expiry(attributes['session_not_on_or_after']) + ) + def retry_login(self): """Retry login if it failed for a temporary error. @@ -719,3 +734,35 @@ logout = csrf_exempt(LogoutView.as_view()) def metadata(request, **kwargs): metadata = utils.create_metadata(request) return HttpResponse(metadata, content_type='text/xml') + + +class DebugLoginView(LoginView): + allow_redirect_to_debug = False + + def dispatch(self, request, *args, **kwargs): + if not request.user.is_superuser or not settings.DEBUG: + return HttpResponseForbidden() + request.session['mellon_debug_login'] = True + self.stream = StringIO() + handler = logging.StreamHandler(self.stream) + handler.setLevel(logging.DEBUG) + self.log.root.addHandler(handler) + return super().dispatch(request, *args, **kwargs) + + def authenticate(self, request, login, attributes): + response = super().authenticate(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, *args, **kwargs): + self.log.info('mellon: would login user %s (username %s)', user.get_full_name(), user) + + +debug_login = csrf_exempt(DebugLoginView.as_view()) -- 2.20.1