Add support for SingleSignOnService with HTTP-POST binding

Warning, this changes the return type of `prepare_for_authentication`
by including the chosen binding, and opens the door for supporting
other SingleSignOnService bindings.
This commit is contained in:
Erick Tryzelaar
2014-12-24 20:13:03 -08:00
parent d38e94715b
commit f4169837c1
3 changed files with 31 additions and 13 deletions

View File

@@ -42,7 +42,7 @@ class Saml2Client(Base):
""" The basic pySAML2 service provider class """ """ The basic pySAML2 service provider class """
def prepare_for_authenticate(self, entityid=None, relay_state="", def prepare_for_authenticate(self, entityid=None, relay_state="",
binding=saml2.BINDING_HTTP_REDIRECT, vorg="", binding=None, vorg="",
nameid_format=None, nameid_format=None,
scoping=None, consent=None, extensions=None, scoping=None, consent=None, extensions=None,
sign=None, sign=None,
@@ -64,20 +64,31 @@ class Saml2Client(Base):
:return: session id and AuthnRequest info :return: session id and AuthnRequest info
""" """
destination = self._sso_location(entityid, binding) expected_binding = binding
reqid, req = self.create_authn_request(destination, vorg, scoping, for binding in [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]:
response_binding, nameid_format, if expected_binding and binding != expected_binding:
consent=consent, continue
extensions=extensions, sign=sign,
**kwargs)
_req_str = "%s" % req
logger.info("AuthNReq: %s" % _req_str) destination = self._sso_location(entityid, binding)
logger.info("destination to provider: %s" % destination)
info = self.apply_binding(binding, _req_str, destination, relay_state) reqid, request = self.create_authn_request(
destination, vorg, scoping, response_binding, nameid_format,
consent=consent,
extensions=extensions, sign=sign,
**kwargs)
return reqid, info _req_str = str(request)
logger.info("AuthNReq: %s" % _req_str)
http_info = self.apply_binding(binding, _req_str, destination,
relay_state)
return reqid, binding, http_info
else:
raise SignonError("No binding available for singon")
def global_logout(self, name_id, reason="", expire=None, sign=None): def global_logout(self, name_id, reason="", expire=None, sign=None):
""" More or less a layer of indirection :-/ """ More or less a layer of indirection :-/

View File

@@ -71,6 +71,10 @@ class VerifyError(SAMLError):
pass pass
class SignonError(SAMLError):
pass
class LogoutError(SAMLError): class LogoutError(SAMLError):
pass pass

View File

@@ -619,10 +619,11 @@ class TestClientWithDummy():
def test_do_authn(self): def test_do_authn(self):
binding = BINDING_HTTP_REDIRECT binding = BINDING_HTTP_REDIRECT
response_binding = BINDING_HTTP_POST response_binding = BINDING_HTTP_POST
sid, http_args = self.client.prepare_for_authenticate( sid, auth_binding, http_args = self.client.prepare_for_authenticate(
IDP, "http://www.example.com/relay_state", IDP, "http://www.example.com/relay_state",
binding=binding, response_binding=response_binding) binding=binding, response_binding=response_binding)
assert binding == auth_binding
assert isinstance(sid, basestring) assert isinstance(sid, basestring)
assert len(http_args) == 4 assert len(http_args) == 4
assert http_args["headers"][0][0] == "Location" assert http_args["headers"][0][0] == "Location"
@@ -669,11 +670,13 @@ class TestClientWithDummy():
def test_post_sso(self): def test_post_sso(self):
binding = BINDING_HTTP_POST binding = BINDING_HTTP_POST
response_binding = BINDING_HTTP_POST response_binding = BINDING_HTTP_POST
sid, http_args = self.client.prepare_for_authenticate( sid, auth_binding, http_args = self.client.prepare_for_authenticate(
"urn:mace:example.com:saml:roland:idp", relay_state="really", "urn:mace:example.com:saml:roland:idp", relay_state="really",
binding=binding, response_binding=response_binding) binding=binding, response_binding=response_binding)
_dic = unpack_form(http_args["data"][3]) _dic = unpack_form(http_args["data"][3])
assert binding == auth_binding
req = self.server.parse_authn_request(_dic["SAMLRequest"], binding) req = self.server.parse_authn_request(_dic["SAMLRequest"], binding)
resp_args = self.server.response_args(req.message, [response_binding]) resp_args = self.server.response_args(req.message, [response_binding])
assert resp_args["binding"] == response_binding assert resp_args["binding"] == response_binding