summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/saml2/client.py12
-rw-r--r--src/saml2/client_base.py9
-rw-r--r--src/saml2/entity.py20
-rw-r--r--src/saml2/mdstore.py66
4 files changed, 74 insertions, 33 deletions
diff --git a/src/saml2/client.py b/src/saml2/client.py
index e283420a..60b108ef 100644
--- a/src/saml2/client.py
+++ b/src/saml2/client.py
@@ -29,7 +29,7 @@ from saml2.client_base import Base
from saml2.client_base import SignOnError
from saml2.client_base import LogoutError
from saml2.client_base import NoServiceDefined
-from saml2.mdstore import destinations
+from saml2.mdstore import locations
import logging
@@ -209,7 +209,7 @@ class Saml2Client(Base):
logger.debug("No SLO '%s' service", binding)
continue
- destination = destinations(srvs)[0]
+ destination = next(locations(srvs), None)
logger.info("destination to provider: %s", destination)
try:
session_info = self.users.get_info_from(name_id,
@@ -374,7 +374,7 @@ class Saml2Client(Base):
name_qualifier=name_qualifier))
srvs = self.metadata.authz_service(entity_id, BINDING_SOAP)
- for dest in destinations(srvs):
+ for dest in locations(srvs):
resp = self._use_soap(dest, "authz_decision_query",
action=action, evidence=evidence,
resource=resource, subject=subject)
@@ -397,7 +397,7 @@ class Saml2Client(Base):
_id_refs = [AssertionIDRef(_id) for _id in assertion_ids]
- for destination in destinations(srvs):
+ for destination in locations(srvs):
res = self._use_soap(destination, "assertion_id_request",
assertion_id_refs=_id_refs, consent=consent,
extensions=extensions, sign=sign)
@@ -411,7 +411,7 @@ class Saml2Client(Base):
srvs = self.metadata.authn_request_service(entity_id, BINDING_SOAP)
- for destination in destinations(srvs):
+ for destination in locations(srvs):
resp = self._use_soap(destination, "authn_query", consent=consent,
extensions=extensions, sign=sign)
if resp:
@@ -461,7 +461,7 @@ class Saml2Client(Base):
if srvs is []:
raise SAMLError("No attribute service support at entity")
- destination = destinations(srvs)[0]
+ destination = next(locations(srvs), None)
if binding == BINDING_SOAP:
return self._use_soap(destination, "attribute_query",
diff --git a/src/saml2/client_base.py b/src/saml2/client_base.py
index 871f3f2c..51a3a574 100644
--- a/src/saml2/client_base.py
+++ b/src/saml2/client_base.py
@@ -12,7 +12,7 @@ import logging
from saml2.entity import Entity
-from saml2.mdstore import destinations
+from saml2.mdstore import locations
from saml2.profile import paos, ecp
from saml2.saml import NAMEID_FORMAT_PERSISTENT
from saml2.saml import NAMEID_FORMAT_TRANSIENT
@@ -212,7 +212,7 @@ class Base(Entity):
# verify that it's in the metadata
srvs = self.metadata.single_sign_on_service(entityid, binding)
if srvs:
- return destinations(srvs)[0]
+ return next(locations(srvs), None)
else:
logger.info("_sso_location: %s, %s", entityid, binding)
raise IdpUnspecified("No IdP to send to given the premises")
@@ -224,9 +224,8 @@ class Base(Entity):
raise IdpUnspecified("Too many IdPs to choose from: %s" % eids)
try:
- srvs = self.metadata.single_sign_on_service(list(eids.keys())[0],
- binding)
- return destinations(srvs)[0]
+ srvs = self.metadata.single_sign_on_service(list(eids.keys())[0], binding)
+ return next(locations(srvs), None)
except IndexError:
raise IdpUnspecified("No IdP to send to given the premises")
diff --git a/src/saml2/entity.py b/src/saml2/entity.py
index fad9326a..c9572aef 100644
--- a/src/saml2/entity.py
+++ b/src/saml2/entity.py
@@ -53,7 +53,7 @@ from saml2.samlp import ArtifactResponse
from saml2.samlp import Artifact
from saml2.samlp import LogoutRequest
from saml2.samlp import AttributeQuery
-from saml2.mdstore import destinations, response_destinations
+from saml2.mdstore import all_locations
from saml2 import BINDING_HTTP_POST
from saml2 import BINDING_HTTP_REDIRECT
from saml2 import BINDING_SOAP
@@ -249,8 +249,9 @@ class Entity(HTTPBase):
return info
- def pick_binding(self, service, bindings=None, descr_type="", request=None,
- entity_id="", response=False):
+ def pick_binding(
+ self, service, bindings=None, descr_type="", request=None, entity_id=""
+ ):
if request and not entity_id:
entity_id = request.issuer.text.strip()
@@ -284,10 +285,8 @@ class Entity(HTTPBase):
if srv["index"] == _index:
return binding, srv["location"]
else:
- if response:
- return binding, response_destinations(srvs)[0]
- else:
- return binding, destinations(srvs)[0]
+ destination = next(all_locations(srvs), None)
+ return binding, destination
except UnsupportedBinding:
pass
@@ -352,10 +351,9 @@ class Entity(HTTPBase):
else:
descr_type = "spsso"
- binding, destination = self.pick_binding(rsrv, bindings,
- descr_type=descr_type,
- request=message,
- response=True)
+ binding, destination = self.pick_binding(
+ rsrv, bindings, descr_type=descr_type, request=message
+ )
info["binding"] = binding
info["destination"] = destination
diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py
index 41e521ec..3dfd0e5a 100644
--- a/src/saml2/mdstore.py
+++ b/src/saml2/mdstore.py
@@ -5,6 +5,8 @@ import json
import logging
import os
import sys
+from itertools import chain
+from warnings import warn as _warn
from hashlib import sha1
from os.path import isfile
@@ -26,7 +28,11 @@ from saml2 import BINDING_SOAP
from saml2.httpbase import HTTPBase
from saml2.extension.idpdisc import BINDING_DISCO
from saml2.extension.idpdisc import DiscoveryResponse
+from saml2.md import NAMESPACE as NS_MD
from saml2.md import EntitiesDescriptor
+from saml2.md import ArtifactResolutionService
+from saml2.md import NameIDMappingService
+from saml2.md import SingleSignOnService
from saml2.mdie import to_dict
from saml2.s_utils import UnsupportedBinding
from saml2.s_utils import UnknownSystemEntity
@@ -70,6 +76,9 @@ classnames = {
ns=NS_MDUI, tag=PrivacyStatementURL.c_tag
),
"mdui_uiinfo_logo": "{ns}&{tag}".format(ns=NS_MDUI, tag=Logo.c_tag),
+ "service_artifact_resolution": "{ns}&{tag}".format(ns=NS_MD, tag=ArtifactResolutionService.c_tag),
+ "service_single_sign_on": "{ns}&{tag}".format(ns=NS_MD, tag=SingleSignOnService.c_tag),
+ "service_nameid_mapping": "{ns}&{tag}".format(ns=NS_MD, tag=NameIDMappingService.c_tag),
}
ENTITY_CATEGORY = "http://macedir.org/entity-category"
@@ -79,8 +88,6 @@ ASSURANCE_CERTIFICATION = "urn:oasis:names:tc:SAML:attribute:assurance-certifica
SAML_METADATA_CONTENT_TYPE = "application/samlmetadata+xml"
DEFAULT_FRESHNESS_PERIOD = "P0Y0M0DT12H0M0S"
-
-
REQ2SRV = {
# IDP
"authn_request": "single_sign_on_service",
@@ -149,17 +156,54 @@ def metadata_modules():
return _res
-def response_destinations(srvs):
- _res = []
- for s in srvs:
- if "response_location" in s:
- _res.append(s["response_location"])
- else:
- _res.append(s["location"])
- return _res
+def response_locations(srvs):
+ """
+ Return the ResponseLocation attributes mapped to the services.
+
+ ArtifactResolutionService, SingleSignOnService and NameIDMappingService MUST omit
+ the ResponseLocation attribute. This is enforced here, but metadata with such
+ service declarations and such attributes should not have been part of the metadata
+ store in the first place.
+ """
+ values = (
+ s["response_location"]
+ for s in srvs
+ if "response_location" in s
+ if s["__class__"] not in [
+ classnames["service_artifact_resolution"],
+ classnames["service_single_sign_on"],
+ classnames["service_nameid_mapping"],
+ ]
+ )
+ return values
+
+
+def locations(srvs):
+ values = (
+ s["location"]
+ for s in srvs
+ if "location" in s
+ )
+ return values
+
def destinations(srvs):
- return [s["location"] for s in srvs]
+ warn_msg = (
+ "`saml2.mdstore.destinations` function is deprecated; "
+ "instead, use `saml2.mdstore.locations` or `saml2.mdstore.all_locations`."
+ )
+ logger.warning(warn_msg)
+ _warn(warn_msg)
+ values = list(locations(srvs))
+ return values
+
+
+def all_locations(srvs):
+ values = chain(
+ response_locations(srvs),
+ locations(srvs),
+ )
+ return values
def attribute_requirement(entity, index=None):