Skip to content

Fix some ECP problems #407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 27 additions & 23 deletions src/saml2/ecp.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

from saml2.response import authn_response

from saml2 import saml

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -53,7 +55,7 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
# ----------------------------------------
# <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
#
Expand All @@ -63,6 +65,19 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):

eelist.append(element_to_extension_element(paos_request))

# ----------------------------------------
# <samlp:AuthnRequest>
# ----------------------------------------

logger.info("entityid: %s, binding: %s" % (entityid, BINDING_SOAP))

location = cls._sso_location(entityid, binding=BINDING_SOAP)
req_id, authn_req = cls.create_authn_request(
location, binding=BINDING_PAOS, service_url_binding=BINDING_PAOS)

body = soapenv.Body()
body.extension_elements = [element_to_extension_element(authn_req)]

# ----------------------------------------
# <ecp:Request>
# ----------------------------------------
Expand All @@ -74,14 +89,16 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
# )
#
# 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))

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>
Expand All @@ -95,19 +112,6 @@ def ecp_auth_request(cls, entityid=None, relay_state="", sign=False):
header = soapenv.Header()
header.extension_elements = eelist

# ----------------------------------------
# <samlp:AuthnRequest>
# ----------------------------------------

logger.info("entityid: %s, binding: %s" % (entityid, BINDING_SOAP))

location = cls._sso_location(entityid, binding=BINDING_SOAP)
req_id, authn_req = cls.create_authn_request(
location, binding=BINDING_PAOS, service_url_binding=BINDING_PAOS)

body = soapenv.Body()
body.extension_elements = [element_to_extension_element(authn_req)]

# ----------------------------------------
# The SOAP envelope
# ----------------------------------------
Expand All @@ -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:
_relay_state = item

response = authn_response(cls.config, cls.service_url(), outstanding,
response = authn_response(cls.config, cls.service_urls(), outstanding,
allow_unsolicited=True)

response.loads("%s" % rdict["body"], False, soap_message)
Expand Down
29 changes: 11 additions & 18 deletions src/saml2/ecp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def phase2(self, authn_request, rc_url, idp_entity_id, headers=None,
if response.status_code != 200:
raise SAMLError(
"Request to IdP failed (%s): %s" % (response.status_code,
response.error))
response.text))

# SAMLP response in a SOAP envelope body, ecp response in headers
respdict = self.parse_soap_message(response.text)
Expand Down Expand Up @@ -200,22 +200,19 @@ def ecp_conversation(self, respdict, idp_entity_id=None):

ht_args = self.use_soap(idp_response, args["rc_url"],
[args["relay_state"]])

ht_args["headers"][0] = ('Content-Type', MIME_PAOS)
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
response = self.send(args["rc_url"], "POST", **ht_args)
response = self.send(**ht_args)

if response.status_code == 302:
# ignore where the SP is redirecting us to and go for the
# url I started off with.
pass
else:
print(response.error)
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)

Expand Down Expand Up @@ -255,22 +252,21 @@ def operation(self, url, idp_entity_id, op, **opargs):
:param opargs: Arguments to the HTTP call
:return: The page
"""
if url not in opargs:
url = self._sp
sp_url = self._sp

# ********************************************
# Phase 1 - First conversation with the SP
# ********************************************
# headers needed to indicate to the SP that I'm ECP enabled

opargs["headers"] = self.add_paos_headers(opargs["headers"])

response = self.send(url, op, **opargs)
logger.debug("[Op] SP response: %s", response)
response = self.send(sp_url, op, **opargs)
logger.debug("[Op] SP response: %s" % response)
print(response.text)

if response.status_code != 200:
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
# body. If so it's the start of the ECP conversation
Expand All @@ -282,19 +278,16 @@ def operation(self, url, idp_entity_id, op, **opargs):
# header blocks may also be present
try:
respdict = self.parse_soap_message(response.text)

self.ecp_conversation(respdict, idp_entity_id)

# should by now be authenticated so this should go smoothly
response = self.send(url, op, **opargs)
except (soap.XmlParseError, AssertionError, KeyError):
pass

#print("RESP",response, self.http.response)

if response.status_code != 404:
if response.status_code >= 400:
raise SAMLError("Error performing operation: %s" % (
response.error,))
response.text,))

return response

Expand Down
5 changes: 3 additions & 2 deletions src/saml2/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from hashlib import sha1

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 class_instances_from_soap_enveloped_saml_thingies
from saml2.soap import open_soap_envelope
Expand Down Expand Up @@ -407,7 +407,8 @@ def parse_soap_message(text):
"""
return class_instances_from_soap_enveloped_saml_thingies(text, [paos,
ecp,
samlp])
samlp,
samlec])

@staticmethod
def unpack_soap_message(text):
Expand Down
14 changes: 14 additions & 0 deletions src/saml2/profile/samlec.py
Original file line number Diff line number Diff line change
@@ -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,
}