Skip to content

Commit a3b26f3

Browse files
committed
Verify signed logout requests with the redirect binding
Signed-off-by: Ivan Kanakarakis <[email protected]>
1 parent 68c0a89 commit a3b26f3

File tree

3 files changed

+102
-18
lines changed

3 files changed

+102
-18
lines changed

src/saml2/client.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,9 @@ def handle_logout_request(
630630
sign=None,
631631
sign_alg=None,
632632
digest_alg=None,
633-
relay_state="",
633+
relay_state=None,
634+
sigalg=None,
635+
signature=None,
634636
):
635637
"""
636638
Deal with a LogoutRequest
@@ -639,6 +641,11 @@ def handle_logout_request(
639641
:param name_id: The id of the current user
640642
:param binding: Which binding the message came in over
641643
:param sign: Whether the response will be signed or not
644+
:param sign_alg: The signing algorithm for the response
645+
:param digest_alg: The digest algorithm for the the response
646+
:param relay_state: The relay state of the request
647+
:param sigalg: The SigAlg query param of the request
648+
:param signature: The Signature query param of the request
642649
:return: Keyword arguments which can be used to send the response
643650
what's returned follow different patterns for different bindings.
644651
If the binding is BINDIND_SOAP, what is returned looks like this::
@@ -652,8 +659,13 @@ def handle_logout_request(
652659
"""
653660
logger.info("logout request: %s", request)
654661

655-
_req = self._parse_request(request, LogoutRequest,
656-
"single_logout_service", binding)
662+
_req = self.parse_logout_request(
663+
xmlstr=request,
664+
binding=binding,
665+
relay_state=relay_state,
666+
sigalg=sigalg,
667+
signature=signature,
668+
)
657669

658670
if _req.message.name_id == name_id:
659671
try:

src/saml2/entity.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,7 +1584,14 @@ def parse_logout_request_response(self, xmlstr, binding=BINDING_SOAP):
15841584

15851585
# ------------------------------------------------------------------------
15861586

1587-
def parse_logout_request(self, xmlstr, binding=BINDING_SOAP):
1587+
def parse_logout_request(
1588+
self,
1589+
xmlstr,
1590+
binding=BINDING_SOAP,
1591+
relay_state=None,
1592+
sigalg=None,
1593+
signature=None,
1594+
):
15881595
""" Deal with a LogoutRequest
15891596
15901597
:param xmlstr: The response as a xml string
@@ -1594,8 +1601,15 @@ def parse_logout_request(self, xmlstr, binding=BINDING_SOAP):
15941601
was not.
15951602
"""
15961603

1597-
return self._parse_request(xmlstr, saml_request.LogoutRequest,
1598-
"single_logout_service", binding)
1604+
return self._parse_request(
1605+
enc_request=xmlstr,
1606+
request_cls=saml_request.LogoutRequest,
1607+
service="single_logout_service",
1608+
binding=binding,
1609+
relay_state=relay_state,
1610+
sigalg=sigalg,
1611+
signature=signature,
1612+
)
15991613

16001614
def use_artifact(self, message, endpoint_index=0):
16011615
"""

tests/test_51_client.py

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,14 +1644,68 @@ def test_do_logout_signed_redirect(self):
16441644
loc = info["headers"][0][1]
16451645
_, _, _, _, qs, _ = parse.urlparse(loc)
16461646
qs = parse.parse_qs(qs)
1647-
assert _leq(qs.keys(),
1648-
['SigAlg', 'SAMLRequest', 'RelayState', 'Signature'])
1647+
assert _leq(qs.keys(), ['SigAlg', 'SAMLRequest', 'RelayState', 'Signature'])
16491648

1650-
assert verify_redirect_signature(list_values2simpletons(qs),
1651-
client.sec.sec_backend)
1649+
qs_simple = list_values2simpletons(qs)
1650+
assert verify_redirect_signature(qs_simple, client.sec.sec_backend)
16521651

1653-
res = self.server.parse_logout_request(qs["SAMLRequest"][0],
1654-
BINDING_HTTP_REDIRECT)
1652+
res = self.server.parse_logout_request(
1653+
qs_simple["SAMLRequest"],
1654+
BINDING_HTTP_REDIRECT,
1655+
relay_state=qs_simple['RelayState'],
1656+
sigalg=qs_simple['SigAlg'],
1657+
signature=qs_simple['Signature'],
1658+
)
1659+
1660+
def test_do_logout_signed_redirect_invalid(self):
1661+
conf = config.SPConfig()
1662+
conf.load_file("sp_slo_redirect_conf")
1663+
client = Saml2Client(conf)
1664+
1665+
session_info = {
1666+
"name_id": nid,
1667+
"issuer": "urn:mace:example.com:saml:roland:idp",
1668+
"not_on_or_after": in_a_while(minutes=15),
1669+
"ava": {
1670+
"givenName": "Anders",
1671+
"sn": "Andersson",
1672+
1673+
}
1674+
}
1675+
client.users.add_information_about_person(session_info)
1676+
entity_ids = client.users.issuers_of_info(nid)
1677+
1678+
resp = client.do_logout(
1679+
nid,
1680+
entity_ids,
1681+
"Tired",
1682+
in_a_while(minutes=5),
1683+
sign=True,
1684+
expected_binding=BINDING_HTTP_REDIRECT,
1685+
)
1686+
1687+
binding, info = resp[entity_ids[0]]
1688+
loc = info["headers"][0][1]
1689+
_, _, _, _, qs, _ = parse.urlparse(loc)
1690+
qs = parse.parse_qs(qs)
1691+
qs_simple = list_values2simpletons(qs)
1692+
1693+
invalid_signature = 'ZEdMZUQ3SjBjQ2ozWmlGaHhyV3JZSzNkTWhQWU02bjA0dzVNeUd1UWgrVDhnYm1oc1R1TTFjPQo='
1694+
qs_simple_invalid = {
1695+
**qs_simple,
1696+
'Signature': invalid_signature,
1697+
}
1698+
assert not verify_redirect_signature(qs_simple_invalid, client.sec.sec_backend)
1699+
1700+
self.server.config.setattr("idp", "want_authn_requests_signed", True)
1701+
with raises(IncorrectlySigned):
1702+
res = self.server.parse_logout_request(
1703+
qs_simple["SAMLRequest"],
1704+
BINDING_HTTP_REDIRECT,
1705+
relay_state=qs_simple['RelayState'],
1706+
sigalg=qs_simple['SigAlg'],
1707+
signature=invalid_signature,
1708+
)
16551709

16561710
def test_do_logout_post(self):
16571711
# information about the user from an IdP
@@ -3245,14 +3299,18 @@ def test_do_logout_signed_redirect(self):
32453299
loc = info["headers"][0][1]
32463300
_, _, _, _, qs, _ = parse.urlparse(loc)
32473301
qs = parse.parse_qs(qs)
3248-
assert _leq(qs.keys(),
3249-
['SigAlg', 'SAMLRequest', 'RelayState', 'Signature'])
3302+
assert _leq(qs.keys(), ['SigAlg', 'SAMLRequest', 'RelayState', 'Signature'])
32503303

3251-
assert verify_redirect_signature(list_values2simpletons(qs),
3252-
client.sec.sec_backend)
3304+
qs_simple = list_values2simpletons(qs)
3305+
assert verify_redirect_signature(qs_simple, client.sec.sec_backend)
32533306

3254-
res = self.server.parse_logout_request(qs["SAMLRequest"][0],
3255-
BINDING_HTTP_REDIRECT)
3307+
res = self.server.parse_logout_request(
3308+
qs_simple["SAMLRequest"],
3309+
BINDING_HTTP_REDIRECT,
3310+
relay_state=qs_simple['RelayState'],
3311+
sigalg=qs_simple['SigAlg'],
3312+
signature=qs_simple['Signature'],
3313+
)
32563314

32573315
def test_do_logout_post(self):
32583316
# information about the user from an IdP

0 commit comments

Comments
 (0)