From 1c558fa98dc97c7505dae9e7b05a0058842a6674 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Thu, 21 May 2020 21:01:49 +0200 Subject: [PATCH] views: ignore XML content in SAML attributes (#43193) --- mellon/views.py | 16 ++++++++++++---- tests/test_sso_slo.py | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/mellon/views.py b/mellon/views.py index 9ebf645..96a6d99 100644 --- a/mellon/views.py +++ b/mellon/views.py @@ -209,16 +209,24 @@ class LoginView(ProfileMixin, LogMixin, View): 'error_redirect_after_timeout': error_redirect_after_timeout, }) + def get_attribute_value(self, attribute, attribute_value): + # check attribute_value contains only text + for node in attribute_value.any: + if not isinstance(node, lasso.MiscTextNode) or not node.textChild: + self.log.warning('unsupported value for attribute %s: "%s"', attribute.name, node.exportToXml()) + return None + return ''.join(lasso_decode(node.content) for node in attribute_value.any) + def sso_success(self, request, login): attributes = {} attribute_statements = login.assertion.attributeStatement for ats in attribute_statements: for at in ats.attribute: values = attributes.setdefault(at.name, []) - for value in at.attributeValue: - contents = [lasso_decode(any.exportToXml()) for any in value.any] - content = ''.join(contents) - values.append(content) + for attribute_value in at.attributeValue: + content = self.get_attribute_value(at, attribute_value) + if content is not None: + values.append(content) attributes['issuer'] = login.remoteProviderId if login.nameIdentifier: name_id = login.nameIdentifier diff --git a/tests/test_sso_slo.py b/tests/test_sso_slo.py index 83a50cc..97285d3 100644 --- a/tests/test_sso_slo.py +++ b/tests/test_sso_slo.py @@ -32,6 +32,7 @@ from django.utils.six.moves.urllib import parse as urlparse from django.utils.encoding import force_str from mellon.utils import create_metadata +from mellon.views import lasso_decode from httmock import all_requests, HTTMock, response as mock_response @@ -121,6 +122,7 @@ class MockIdp(object): break else: attribute = lasso.Saml2Attribute() + attribute.name = name attributes.append(attribute) statement.attribute = attributes attribute_values = list(attribute.attributeValue) @@ -138,6 +140,7 @@ class MockIdp(object): atv.any = value_any add_attribute('email', 'john', '.doe@gmail.com') add_attribute('wtf', 'john', lasso.MiscTextNode.newWithXmlNode('coucou')) + add_attribute('first_name', 'Fr\xe9d\xe9ric') if not auth_result and msg: login.response.status.statusMessage = msg @@ -164,8 +167,8 @@ class MockIdp(object): del self.artifact del self.artifact_message login.buildResponseMsg() - assert 'rsa-sha256' in login.msgBody - return '\n' + login.msgBody + assert 'rsa-sha256' in lasso_decode(login.msgBody) + return '\n' + lasso_decode(login.msgBody) def mock_artifact_resolver(self): @all_requests @@ -204,6 +207,9 @@ def test_sso(db, app, idp, caplog, sp_settings): assert 'created new user' in caplog.text assert 'logged in using SAML' in caplog.text assert urlparse.urlparse(response['Location']).path == sp_settings.LOGIN_REDIRECT_URL + assert app.session['mellon_session']['first_name'] == ['Fr\xe9d\xe9ric'] + assert app.session['mellon_session']['email'] == ['john.doe@gmail.com'] + assert app.session['mellon_session']['wtf'] == [] def test_sso_request_denied(db, app, idp, caplog, sp_settings): -- 2.26.2