utils: add method to build a session dump from models (#69740)

Storing the LassoSession dump in the Django session is no longer needed,
we can rebuild it from the information in the models.
This commit is contained in:
Benjamin Dauvergne 2022-10-03 16:06:03 +02:00
parent 600c8cfbc0
commit 7f9602c528
5 changed files with 45 additions and 43 deletions

View File

@ -1,12 +1,5 @@
<ns0:Session xmlns:ns0="http://www.entrouvert.org/namespaces/lasso/0.0" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" Version="2">
{% for session_info in session_infos %}
<ns0:NidAndSessionIndex AssertionID=""
ProviderID="{{ session_info.entity_id }}"
SessionIndex="{{ session_info.session_index }}">
<ns1:NameID Format="{{ session_info.name_id_format }}"
{% if session_info.name_id_name_qualifier %}NameQualifier="{{ session_info.name_id_name_qualifier }}"{% endif %}
{% if session_info.name_id_sp_name_qualifier %}SPNameQualifier="{{ session_info.name_id_sp_name_qualifier }}"{% endif %}
>{{ session_info.name_id_content }}</ns1:NameID>
</ns0:NidAndSessionIndex>
{% endfor %}
<ns0:Session xmlns:ns0="http://www.entrouvert.org/namespaces/lasso/0.0" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" Version="2">{% for session_index in session_indexes %}{% with nameid=session_index.saml_identifier %}
<ns0:NidAndSessionIndex AssertionID="" ProviderID="{{ nameid.issuer.entity_id }}" SessionIndex="{{ session_index.session_index }}">
<ns1:NameID Format="{{ nameid.nid_format }}"{% if nameid.nid_name_qualifier %} NameQualifier="{{ nameid.nid_name_qualifier }}"{% endif %}{% if nameid.nid_sp_name_qualifier %} SPNameQualifier="{{ nameid.nid_sp_name_qualifier }}"{% endif %}{% if nameid.nid_sp_provided_id %} SPProvidedId="{{ nameid.nid_sp_provided_id}}"{% endif %}>{{ nameid.name_id }}</ns1:NameID>
</ns0:NidAndSessionIndex>{% endwith %}{% endfor %}
</ns0:Session>

View File

@ -211,26 +211,8 @@ def get_setting(idp, name, default=None):
return idp.get(name) or getattr(app_settings, name, default)
def make_session_dump(lasso_name_id, indexes):
session_infos = []
name_id = force_str(lasso_name_id.content)
name_id_format = force_str(lasso_name_id.format)
name_qualifier = lasso_name_id.nameQualifier and force_str(lasso_name_id.nameQualifier)
sp_name_qualifier = lasso_name_id.spNameQualifier and force_str(lasso_name_id.spNameQualifier)
for index in indexes:
issuer = index.saml_identifier.issuer.entity_id
session_infos.append(
{
'entity_id': issuer,
'session_index': index.session_index,
'name_id_content': name_id,
'name_id_format': name_id_format,
'name_id_name_qualifier': name_qualifier,
'name_id_sp_name_qualifier': sp_name_qualifier,
}
)
session_dump = render_to_string('mellon/session_dump.xml', {'session_infos': session_infos})
return session_dump
def make_session_dump(session_indexes):
return render_to_string('mellon/session_dump.xml', {'session_indexes': session_indexes})
def create_logout(request):

View File

@ -318,8 +318,6 @@ class LoginView(ProfileMixin, LogMixin, View):
else:
self.log.warning('no user found for NameID %r', attributes['name_id_content'])
return self.render(request, 'mellon/user_not_found.html', {'saml_attributes': attributes})
request.session['lasso_session_dump'] = login.session.dump()
return HttpResponseRedirect(next_url)
def render_debug_template(self, request, login, attributes=None):
@ -698,7 +696,7 @@ class LogoutView(ProfileMixin, LogMixin, View):
indexes = indexes.filter(session_index__in=session_indexes)
# lasso has too much state :/
logout.setSessionFromDump(utils.make_session_dump(logout.nameIdentifier, indexes))
logout.setSessionFromDump(utils.make_session_dump(indexes))
try:
logout.validateRequest()
@ -743,10 +741,14 @@ class LogoutView(ProfileMixin, LogMixin, View):
self.profile = logout = utils.create_logout(request)
self.get_relay_state(create=True)
try:
if 'lasso_session_dump' in request.session:
logout.setSessionFromDump(request.session['lasso_session_dump'])
else:
session_indexes = models.SessionIndex.objects.filter(
saml_identifier__user=request.user, saml_identifier__issuer__entity_id=issuer
).order_by('-id')[:1]
if not session_indexes:
self.log.error('unable to find lasso session dump')
else:
session_dump = utils.make_session_dump(session_indexes)
logout.setSessionFromDump(session_dump)
logout.initRequest(issuer, lasso.HTTP_METHOD_REDIRECT)
logout.buildRequestMsg()
except lasso.Error as e:

View File

@ -270,7 +270,7 @@ def test_sso_slo_next(db, app, idp, caplog, sp_settings):
assert response.location == '/some/path/'
def test_sso_slo_default_next_url(db, app, idp, caplog, sp_settings, rf):
def test_sso_slo_default_next_url(db, app, idp, caplog, sp_settings, rf, django_user_model):
from mellon.views import logout
response = app.get(reverse('mellon_login'))
@ -279,8 +279,7 @@ def test_sso_slo_default_next_url(db, app, idp, caplog, sp_settings, rf):
request = rf.get('/logout/')
request.session = app.session
request.user = mock.Mock()
request.user.is_authenticated = True
request.user = django_user_model.objects.get()
response = logout(request, logout_next_url='/other/path/')
assert list(request.session.values()) == ['/other/path/']
@ -290,8 +289,7 @@ def test_sso_slo_default_next_url(db, app, idp, caplog, sp_settings, rf):
request = rf.get('/logout/?next=/some/path/')
request.session = app.session
request.user = mock.Mock()
request.user.is_authenticated = True
request.user = django_user_model.objects.get()
response = logout(request, logout_next_url='/other/path/')
assert list(request.session.values()) == ['/some/path/']

View File

@ -22,7 +22,7 @@ from xml_utils import assert_xml_constraints
from mellon.models import Issuer
from mellon.models_utils import get_issuer
from mellon.utils import create_metadata, flatten_datetime, iso8601_to_datetime
from mellon.utils import create_metadata, flatten_datetime, iso8601_to_datetime, make_session_dump
from mellon.views import check_next_url
@ -236,3 +236,30 @@ def test_get_issuer_entity_id_migration(db, settings, metadata):
assert issuer3.id == issuer1.id
assert issuer3.entity_id == entity_id2
assert issuer3.slug == 'idp'
def test_make_session_dump(db, django_user_model):
from mellon.models import SessionIndex, UserSAMLIdentifier
user = django_user_model.objects.create(username='user')
issuer = Issuer.objects.create(entity_id='https://idp.example.com/metadata', slug='idp')
saml_identifier = UserSAMLIdentifier.objects.create(
user=user, name_id='1234', issuer=issuer, nid_format=lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT
)
for i in range(2):
SessionIndex.objects.create(session_index=str(i), saml_identifier=saml_identifier, session_key='abcd')
assert (
make_session_dump(saml_identifier.sessionindex_set.all())
== '''\
<ns0:Session xmlns:ns0="http://www.entrouvert.org/namespaces/lasso/0.0" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" Version="2">
<ns0:NidAndSessionIndex AssertionID="" ProviderID="https://idp.example.com/metadata" SessionIndex="0">
<ns1:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">1234</ns1:NameID>
</ns0:NidAndSessionIndex>
<ns0:NidAndSessionIndex AssertionID="" ProviderID="https://idp.example.com/metadata" SessionIndex="1">
<ns1:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">1234</ns1:NameID>
</ns0:NidAndSessionIndex>
</ns0:Session>
'''
)