From ff8373ee6a00fe8664d1771277a0ae7099c49081 Mon Sep 17 00:00:00 2001 From: Valentin Samir Date: Sun, 29 Apr 2018 18:48:41 +0200 Subject: [PATCH] Always return authenticationDate, longTermAuthenticationRequestTokenUsed and isFromNewLogin attributes As specified in the CAS response XML schema (see Appendix A). Fix #37 as returned attributes are now never empty. --- cas_server/cas.py | 2 +- cas_server/templates/cas_server/samlValidate.xml | 9 +++++++++ .../templates/cas_server/serviceValidate.xml | 6 ++++++ cas_server/tests/mixin.py | 14 +++++++++++--- cas_server/tests/test_view.py | 4 +++- cas_server/tests/utils.py | 6 +++++- cas_server/views.py | 9 +++++++-- 7 files changed, 42 insertions(+), 8 deletions(-) diff --git a/cas_server/cas.py b/cas_server/cas.py index 06ce8d2..3ec08bc 100644 --- a/cas_server/cas.py +++ b/cas_server/cas.py @@ -206,7 +206,7 @@ class CASClientV2(CASClientBase, ReturnUnicode): def parse_attributes_xml_element(cls, element, charset): attributes = dict() for attribute in element: - tag = cls.self.u(attribute.tag, charset).split(u"}").pop() + tag = cls.u(attribute.tag, charset).split(u"}").pop() if tag in attributes: if isinstance(attributes[tag], list): attributes[tag].append(cls.u(attribute.text, charset)) diff --git a/cas_server/templates/cas_server/samlValidate.xml b/cas_server/templates/cas_server/samlValidate.xml index 63d5cb2..d61bed1 100644 --- a/cas_server/templates/cas_server/samlValidate.xml +++ b/cas_server/templates/cas_server/samlValidate.xml @@ -29,6 +29,15 @@ + + {{auth_date}} + + + false{# we do not support long-term (Remember-Me) auth #} + + + {{is_new_login}} + {% for name, value in attributes %} {{value}} diff --git a/cas_server/templates/cas_server/serviceValidate.xml b/cas_server/templates/cas_server/serviceValidate.xml index e41a022..f583dbe 100644 --- a/cas_server/templates/cas_server/serviceValidate.xml +++ b/cas_server/templates/cas_server/serviceValidate.xml @@ -2,8 +2,14 @@ {{username}} + {{auth_date}} + false{# we do not support long-term (Remember-Me) auth #} + {{is_new_login}} {% for key, value in attributes %} {{value}} {% endfor %} + + + {% for key, value in attributes %} {% endfor %}{% if proxyGrantingTicket %} {{proxyGrantingTicket}} {% endif %}{% if proxies %} diff --git a/cas_server/tests/mixin.py b/cas_server/tests/mixin.py index d791b53..e6930a7 100644 --- a/cas_server/tests/mixin.py +++ b/cas_server/tests/mixin.py @@ -149,15 +149,23 @@ class XmlContent(object): namespaces={'cas': "http://www.yale.edu/tp/cas"} ) self.assertEqual(len(attributes), 1) + ignore_attrs = {"authenticationDate", "longTermAuthenticationRequestTokenUsed", "isFromNewLogin"} + ignored_attrs = 0 attrs1 = set() for attr in attributes[0]: - attrs1.add((attr.tag[len("http://www.yale.edu/tp/cas")+2:], attr.text)) + name = attr.tag[len("http://www.yale.edu/tp/cas")+2:] + if not name in ignore_attrs: + attrs1.add((name, attr.text)) + else: + ignored_attrs += 1 attributes = root.xpath("//cas:attribute", namespaces={'cas': "http://www.yale.edu/tp/cas"}) - self.assertEqual(len(attributes), len(attrs1)) + self.assertEqual(len(attributes), len(attrs1) + ignored_attrs) attrs2 = set() for attr in attributes: - attrs2.add((attr.attrib['name'], attr.attrib['value'])) + name = attr.attrib['name'] + if not name in ignore_attrs: + attrs2.add((name, attr.attrib['value'])) original = set() for key, value in original_attributes.items(): if isinstance(value, list): diff --git a/cas_server/tests/test_view.py b/cas_server/tests/test_view.py index 016db3e..ebdb8bd 100644 --- a/cas_server/tests/test_view.py +++ b/cas_server/tests/test_view.py @@ -1907,9 +1907,11 @@ class SamlValidateTestCase(TestCase, BaseServicePattern, XmlContent): "//samla:AttributeStatement/samla:Attribute", namespaces={'samla': "urn:oasis:names:tc:SAML:1.0:assertion"} ) + ignore_attrs = {"authenticationDate", "longTermAuthenticationRequestTokenUsed", "isFromNewLogin"} - set(original_attributes.keys()) attrs = set() for attr in attributes: - attrs.add((attr.attrib['AttributeName'], attr.getchildren()[0].text)) + if not attr.attrib['AttributeName'] in ignore_attrs: + attrs.add((attr.attrib['AttributeName'], attr.getchildren()[0].text)) original = set() for key, value in original_attributes.items(): if isinstance(value, list): diff --git a/cas_server/tests/utils.py b/cas_server/tests/utils.py index bee39cf..3c74516 100644 --- a/cas_server/tests/utils.py +++ b/cas_server/tests/utils.py @@ -264,7 +264,9 @@ class DummyCAS(BaseHTTPServer.BaseHTTPRequestHandler): template = loader.get_template('cas_server/serviceValidate.xml') context = Context({ 'username': self.server.username, - 'attributes': self.server.attributes + 'attributes': self.server.attributes, + 'auth_date': timezone.now().replace(microsecond=0).isoformat(), + 'is_new_login': 'true', }) self.wfile.write(return_bytes(template.render(context), "utf8")) else: @@ -301,6 +303,8 @@ class DummyCAS(BaseHTTPServer.BaseHTTPRequestHandler): 'ResponseID': utils.gen_saml_id(), 'username': self.server.username, 'attributes': self.server.attributes, + 'auth_date': timezone.now().replace(microsecond=0).isoformat(), + 'is_new_login': 'true', }) self.wfile.write(return_bytes(template.render(context), "utf8")) else: diff --git a/cas_server/views.py b/cas_server/views.py index 9bd7606..3af4077 100644 --- a/cas_server/views.py +++ b/cas_server/views.py @@ -1153,7 +1153,9 @@ class ValidateService(View): params = { 'username': self.ticket.username(), 'attributes': self.ticket.attributs_flat(), - 'proxies': proxies + 'proxies': proxies, + 'auth_date': self.ticket.user.last_login.replace(microsecond=0).isoformat(), + 'is_new_login': 'true' if self.ticket.renew else 'false' } # if pgtUrl is set, require https or localhost if self.pgt_url and ( @@ -1415,7 +1417,10 @@ class SamlValidate(CsrfExemptView): 'Recipient': self.target, 'ResponseID': utils.gen_saml_id(), 'username': self.ticket.username(), - 'attributes': self.ticket.attributs_flat() + 'attributes': self.ticket.attributs_flat(), + 'auth_date': self.ticket.user.last_login.replace(microsecond=0).isoformat(), + 'is_new_login': 'true' if self.ticket.renew else 'false' + } logger.info( "SamlValidate: ticket %s validated for user %s on service %s." % (