Merge pull request #407 from cemiarni/master

Fix some ECP problems
This commit is contained in:
Roland Hedberg 2017-04-24 16:00:02 +02:00 committed by GitHub
commit 9e0f8afece
4 changed files with 66 additions and 54 deletions

View File

@ -24,6 +24,8 @@ from saml2.schema import soapenv
from saml2.response import authn_response from saml2.response import authn_response
from saml2 import saml
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -53,7 +55,7 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
# ---------------------------------------- # ----------------------------------------
# <paos:Request> # <paos:Request>
# ---------------------------------------- # ----------------------------------------
my_url = cls.service_url(BINDING_PAOS) my_url = cls.service_urls(BINDING_PAOS)[0]
# must_understand and actor according to the standard # must_understand and actor according to the standard
# #
@ -63,38 +65,6 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
eelist.append(element_to_extension_element(paos_request)) eelist.append(element_to_extension_element(paos_request))
# ----------------------------------------
# <ecp:Request>
# ----------------------------------------
# idp = samlp.IDPEntry(
# provider_id = "https://idp.example.org/entity",
# name = "Example identity provider",
# loc = "https://idp.example.org/saml2/sso",
# )
#
# idp_list = samlp.IDPList(idp_entry= [idp])
#
# ecp_request = ecp.Request(
# actor = ACTOR, must_understand = "1",
# provider_name = "Example Service Provider",
# issuer=saml.Issuer(text="https://sp.example.org/entity"),
# idp_list = idp_list)
#
# eelist.append(element_to_extension_element(ecp_request))
# ----------------------------------------
# <ecp:RelayState>
# ----------------------------------------
relay_state = ecp.RelayState(actor=ACTOR, must_understand="1",
text=relay_state)
eelist.append(element_to_extension_element(relay_state))
header = soapenv.Header()
header.extension_elements = eelist
# ---------------------------------------- # ----------------------------------------
# <samlp:AuthnRequest> # <samlp:AuthnRequest>
# ---------------------------------------- # ----------------------------------------
@ -108,6 +78,40 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
body = soapenv.Body() body = soapenv.Body()
body.extension_elements = [element_to_extension_element(authn_req)] body.extension_elements = [element_to_extension_element(authn_req)]
# ----------------------------------------
# <ecp:Request>
# ----------------------------------------
# idp = samlp.IDPEntry(
# provider_id = "https://idp.example.org/entity",
# name = "Example identity provider",
# loc = "https://idp.example.org/saml2/sso",
# )
#
# idp_list = samlp.IDPList(idp_entry= [idp])
idp_list = None
ecp_request = ecp.Request(
actor=ACTOR,
must_understand="1",
provider_name=None,
issuer=saml.Issuer(text=authn_req.issuer.text),
idp_list=idp_list)
eelist.append(element_to_extension_element(ecp_request))
# ----------------------------------------
# <ecp:RelayState>
# ----------------------------------------
relay_state = ecp.RelayState(actor=ACTOR, must_understand="1",
text=relay_state)
eelist.append(element_to_extension_element(relay_state))
header = soapenv.Header()
header.extension_elements = eelist
# ---------------------------------------- # ----------------------------------------
# The SOAP envelope # The SOAP envelope
# ---------------------------------------- # ----------------------------------------
@ -126,7 +130,7 @@ def handle_ecp_authn_response(cls, soap_message, outstanding=None):
if item.c_tag == "RelayState" and item.c_namespace == ecp.NAMESPACE: if item.c_tag == "RelayState" and item.c_namespace == ecp.NAMESPACE:
_relay_state = item _relay_state = item
response = authn_response(cls.config, cls.service_url(), outstanding, response = authn_response(cls.config, cls.service_urls(), outstanding,
allow_unsolicited=True) allow_unsolicited=True)
response.loads("%s" % rdict["body"], False, soap_message) response.loads("%s" % rdict["body"], False, soap_message)

View File

@ -119,7 +119,7 @@ class Client(Entity):
if response.status_code != 200: if response.status_code != 200:
raise SAMLError( raise SAMLError(
"Request to IdP failed (%s): %s" % (response.status_code, "Request to IdP failed (%s): %s" % (response.status_code,
response.error)) response.text))
# SAMLP response in a SOAP envelope body, ecp response in headers # SAMLP response in a SOAP envelope body, ecp response in headers
respdict = self.parse_soap_message(response.text) respdict = self.parse_soap_message(response.text)
@ -200,22 +200,19 @@ class Client(Entity):
ht_args = self.use_soap(idp_response, args["rc_url"], ht_args = self.use_soap(idp_response, args["rc_url"],
[args["relay_state"]]) [args["relay_state"]])
ht_args["headers"][0] = ('Content-Type', MIME_PAOS)
logger.debug("[P3] Post to SP: %s", ht_args["data"]) logger.debug("[P3] Post to SP: %s", ht_args["data"])
ht_args["headers"].append(('Content-Type', 'application/vnd.paos+xml'))
# POST the package from the IdP to the SP # POST the package from the IdP to the SP
response = self.send(args["rc_url"], "POST", **ht_args) response = self.send(**ht_args)
if response.status_code == 302: if response.status_code == 302:
# ignore where the SP is redirecting us to and go for the # ignore where the SP is redirecting us to and go for the
# url I started off with. # url I started off with.
pass pass
else: else:
print(response.error)
raise SAMLError( raise SAMLError(
"Error POSTing package to SP: %s" % response.error) "Error POSTing package to SP: %s" % response.text)
logger.debug("[P3] SP response: %s", response.text) logger.debug("[P3] SP response: %s", response.text)
@ -255,8 +252,7 @@ class Client(Entity):
:param opargs: Arguments to the HTTP call :param opargs: Arguments to the HTTP call
:return: The page :return: The page
""" """
if url not in opargs: sp_url = self._sp
url = self._sp
# ******************************************** # ********************************************
# Phase 1 - First conversation with the SP # Phase 1 - First conversation with the SP
@ -264,13 +260,13 @@ class Client(Entity):
# headers needed to indicate to the SP that I'm ECP enabled # headers needed to indicate to the SP that I'm ECP enabled
opargs["headers"] = self.add_paos_headers(opargs["headers"]) opargs["headers"] = self.add_paos_headers(opargs["headers"])
response = self.send(sp_url, op, **opargs)
response = self.send(url, op, **opargs) logger.debug("[Op] SP response: %s" % response)
logger.debug("[Op] SP response: %s", response) print(response.text)
if response.status_code != 200: if response.status_code != 200:
raise SAMLError( raise SAMLError(
"Request to SP failed: %s" % response.error) "Request to SP failed: %s" % response.text)
# The response might be a AuthnRequest instance in a SOAP envelope # The response might be a AuthnRequest instance in a SOAP envelope
# body. If so it's the start of the ECP conversation # body. If so it's the start of the ECP conversation
@ -282,7 +278,6 @@ class Client(Entity):
# header blocks may also be present # header blocks may also be present
try: try:
respdict = self.parse_soap_message(response.text) respdict = self.parse_soap_message(response.text)
self.ecp_conversation(respdict, idp_entity_id) self.ecp_conversation(respdict, idp_entity_id)
# should by now be authenticated so this should go smoothly # should by now be authenticated so this should go smoothly
@ -290,11 +285,9 @@ class Client(Entity):
except (soap.XmlParseError, AssertionError, KeyError): except (soap.XmlParseError, AssertionError, KeyError):
pass pass
#print("RESP",response, self.http.response) if response.status_code >= 400:
if response.status_code != 404:
raise SAMLError("Error performing operation: %s" % ( raise SAMLError("Error performing operation: %s" % (
response.error,)) response.text,))
return response return response

View File

@ -8,7 +8,7 @@ from binascii import hexlify
from hashlib import sha1 from hashlib import sha1
from saml2.metadata import ENDPOINTS from saml2.metadata import ENDPOINTS
from saml2.profile import paos, ecp from saml2.profile import paos, ecp, samlec
from saml2.soap import parse_soap_enveloped_saml_artifact_resolve from saml2.soap import parse_soap_enveloped_saml_artifact_resolve
from saml2.soap import class_instances_from_soap_enveloped_saml_thingies from saml2.soap import class_instances_from_soap_enveloped_saml_thingies
from saml2.soap import open_soap_envelope from saml2.soap import open_soap_envelope
@ -407,7 +407,8 @@ class Entity(HTTPBase):
""" """
return class_instances_from_soap_enveloped_saml_thingies(text, [paos, return class_instances_from_soap_enveloped_saml_thingies(text, [paos,
ecp, ecp,
samlp]) samlp,
samlec])
@staticmethod @staticmethod
def unpack_soap_message(text): def unpack_soap_message(text):

View File

@ -0,0 +1,14 @@
from saml2 import SamlBase
NAMESPACE = 'urn:ietf:params:xml:ns:samlec'
class GeneratedKey(SamlBase):
c_tag = 'GeneratedKey'
c_namespace = NAMESPACE
ELEMENT_BY_TAG = {
'GeneratedKey': GeneratedKey,
}