summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/saml2/attributemaps/saml_uri.py17
-rw-r--r--src/saml2/client_base.py66
-rw-r--r--src/saml2/config.py3
-rw-r--r--src/saml2/extension/requested_attributes.py131
-rw-r--r--src/saml2/extension/sp_type.py54
-rw-r--r--src/saml2/metadata.py12
6 files changed, 282 insertions, 1 deletions
diff --git a/src/saml2/attributemaps/saml_uri.py b/src/saml2/attributemaps/saml_uri.py
index ca6dfd84..e97090ff 100644
--- a/src/saml2/attributemaps/saml_uri.py
+++ b/src/saml2/attributemaps/saml_uri.py
@@ -13,10 +13,19 @@ SCHAC = 'urn:oid:1.3.6.1.4.1.25178.1.2.'
SIS = 'urn:oid:1.2.752.194.10.2.'
UMICH = 'urn:oid:1.3.6.1.4.1.250.1.57.'
OPENOSI_OID = 'urn:oid:1.3.6.1.4.1.27630.2.1.1.' #openosi-0.82.schema http://www.openosi.org/osi/display/ldap/Home
+EIDAS_NATURALPERSON = 'http://eidas.europa.eu/attributes/naturalperson/'
MAP = {
'identifier': 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
'fro': {
+ EIDAS_NATURALPERSON+'PersonIdentifier': 'PersonIdentifier',
+ EIDAS_NATURALPERSON+'FamilyName': 'FamilyName',
+ EIDAS_NATURALPERSON+'FirstName': 'FirstName',
+ EIDAS_NATURALPERSON+'DateOfBirth': 'DateOfBirth',
+ EIDAS_NATURALPERSON+'BirthName': 'BirthName',
+ EIDAS_NATURALPERSON+'PlaceOfBirth': 'PlaceOfBirth',
+ EIDAS_NATURALPERSON+'CurrentAddress': 'CurrentAddress',
+ EIDAS_NATURALPERSON+'Gender': 'Gender',
EDUCOURSE_OID+'1': 'eduCourseOffering',
EDUCOURSE_OID+'2': 'eduCourseMember',
EDUMEMBER1_OID+'1': 'isMemberOf',
@@ -161,6 +170,14 @@ MAP = {
X500ATTR_OID+'65': 'pseudonym',
},
'to': {
+ 'PersonIdentifier': EIDAS_NATURALPERSON+'PersonIdentifier',
+ 'FamilyName': EIDAS_NATURALPERSON+'FamilyName',
+ 'FirstName': EIDAS_NATURALPERSON+'FirstName',
+ 'DateOfBirth': EIDAS_NATURALPERSON+'DateOfBirth',
+ 'BirthName': EIDAS_NATURALPERSON+'BirthName',
+ 'PlaceOfBirth': EIDAS_NATURALPERSON+'PlaceOfBirth',
+ 'CurrentAddress': EIDAS_NATURALPERSON+'CurrentAddress',
+ 'Gender': EIDAS_NATURALPERSON+'Gender',
'associatedDomain': UCL_DIR_PILOT+'37',
'authorityRevocationList': X500ATTR_OID+'38',
'businessCategory': X500ATTR_OID+'15',
diff --git a/src/saml2/client_base.py b/src/saml2/client_base.py
index 50b457d1..531ddea5 100644
--- a/src/saml2/client_base.py
+++ b/src/saml2/client_base.py
@@ -10,6 +10,8 @@ import six
from saml2.entity import Entity
+import saml2.attributemaps as attributemaps
+
from saml2.mdstore import destinations
from saml2.profile import paos, ecp
from saml2.saml import NAMEID_FORMAT_TRANSIENT
@@ -18,6 +20,9 @@ from saml2.samlp import NameIDMappingRequest
from saml2.samlp import AttributeQuery
from saml2.samlp import AuthzDecisionQuery
from saml2.samlp import AuthnRequest
+from saml2.samlp import Extensions
+from saml2.extension import sp_type
+from saml2.extension import requested_attributes
import saml2
import time
@@ -347,6 +352,67 @@ class Base(Entity):
if force_authn:
args['force_authn'] = 'true'
+ conf_sp_type = self.config.getattr('sp_type', 'sp')
+ conf_sp_type_in_md = self.config.getattr('sp_type_in_metadata', 'sp')
+ if conf_sp_type and conf_sp_type_in_md is False:
+ if not extensions:
+ extensions = Extensions()
+ item = sp_type.SPType(text=conf_sp_type)
+ extensions.add_extension_element(item)
+
+ requested_attrs = self.config.getattr('requested_attributes', 'sp')
+ if requested_attrs:
+ if not extensions:
+ extensions = Extensions()
+
+ attributemapsmods = []
+ for modname in attributemaps.__all__:
+ attributemapsmods.append(getattr(attributemaps, modname))
+
+ items = []
+ for attr in requested_attrs:
+ friendly_name = attr.get('friendly_name')
+ name = attr.get('name')
+ name_format = attr.get('name_format')
+ is_required = str(attr.get('required', False)).lower()
+
+ if not name and not friendly_name:
+ raise ValueError(
+ "Missing required attribute: '{}' or '{}'".format(
+ 'name', 'friendly_name'))
+
+ if not name:
+ for mod in attributemapsmods:
+ try:
+ name = mod.MAP['to'][friendly_name]
+ except KeyError:
+ continue
+ else:
+ if not name_format:
+ name_format = mod.MAP['identifier']
+ break
+
+ if not friendly_name:
+ for mod in attributemapsmods:
+ try:
+ friendly_name = mod.MAP['fro'][name]
+ except KeyError:
+ continue
+ else:
+ if not name_format:
+ name_format = mod.MAP['identifier']
+ break
+
+ items.append(requested_attributes.RequestedAttribute(
+ is_required=is_required,
+ name_format=name_format,
+ friendly_name=friendly_name,
+ name=name))
+
+ item = requested_attributes.RequestedAttributes(
+ extension_elements=items)
+ extensions.add_extension_element(item)
+
if kwargs:
_args, extensions = self._filter_args(AuthnRequest(), extensions,
**kwargs)
diff --git a/src/saml2/config.py b/src/saml2/config.py
index e508a954..296f0e85 100644
--- a/src/saml2/config.py
+++ b/src/saml2/config.py
@@ -78,6 +78,9 @@ SP_ARGS = [
"requested_attribute_name_format",
"hide_assertion_consumer_service",
"force_authn",
+ "sp_type",
+ "sp_type_in_metadata",
+ "requested_attributes",
]
AA_IDP_ARGS = [
diff --git a/src/saml2/extension/requested_attributes.py b/src/saml2/extension/requested_attributes.py
new file mode 100644
index 00000000..3d574f15
--- /dev/null
+++ b/src/saml2/extension/requested_attributes.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# Generated Tue Jul 18 14:58:29 2017 by parse_xsd.py version 0.5.
+#
+
+import saml2
+from saml2 import SamlBase
+
+from saml2 import saml
+
+
+NAMESPACE = 'http://eidas.europa.eu/saml-extensions'
+
+class RequestedAttributeType_(SamlBase):
+ """The http://eidas.europa.eu/saml-extensions:RequestedAttributeType element """
+
+ c_tag = 'RequestedAttributeType'
+ c_namespace = NAMESPACE
+ c_children = SamlBase.c_children.copy()
+ c_attributes = SamlBase.c_attributes.copy()
+ c_child_order = SamlBase.c_child_order[:]
+ c_cardinality = SamlBase.c_cardinality.copy()
+ c_children['{urn:oasis:names:tc:SAML:2.0:assertion}AttributeValue'] = ('attribute_value', [saml.AttributeValue])
+ c_cardinality['attribute_value'] = {"min":0}
+ c_attributes['Name'] = ('name', 'None', True)
+ c_attributes['NameFormat'] = ('name_format', 'None', True)
+ c_attributes['FriendlyName'] = ('friendly_name', 'None', False)
+ c_attributes['isRequired'] = ('is_required', 'None', False)
+ c_child_order.extend(['attribute_value'])
+
+ def __init__(self,
+ attribute_value=None,
+ name=None,
+ name_format=None,
+ friendly_name=None,
+ is_required=None,
+ text=None,
+ extension_elements=None,
+ extension_attributes=None,
+ ):
+ SamlBase.__init__(self,
+ text=text,
+ extension_elements=extension_elements,
+ extension_attributes=extension_attributes,
+ )
+ self.attribute_value=attribute_value or []
+ self.name=name
+ self.name_format=name_format
+ self.friendly_name=friendly_name
+ self.is_required=is_required
+
+def requested_attribute_type__from_string(xml_string):
+ return saml2.create_class_from_xml_string(RequestedAttributeType_, xml_string)
+
+
+class RequestedAttribute(RequestedAttributeType_):
+ """The http://eidas.europa.eu/saml-extensions:RequestedAttribute element """
+
+ c_tag = 'RequestedAttribute'
+ c_namespace = NAMESPACE
+ c_children = RequestedAttributeType_.c_children.copy()
+ c_attributes = RequestedAttributeType_.c_attributes.copy()
+ c_child_order = RequestedAttributeType_.c_child_order[:]
+ c_cardinality = RequestedAttributeType_.c_cardinality.copy()
+
+def requested_attribute_from_string(xml_string):
+ return saml2.create_class_from_xml_string(RequestedAttribute, xml_string)
+
+
+class RequestedAttributesType_(SamlBase):
+ """The http://eidas.europa.eu/saml-extensions:RequestedAttributesType element """
+
+ c_tag = 'RequestedAttributesType'
+ c_namespace = NAMESPACE
+ c_children = SamlBase.c_children.copy()
+ c_attributes = SamlBase.c_attributes.copy()
+ c_child_order = SamlBase.c_child_order[:]
+ c_cardinality = SamlBase.c_cardinality.copy()
+ c_children['{http://eidas.europa.eu/saml-extensions}RequestedAttribute'] = ('requested_attribute', [RequestedAttribute])
+ c_cardinality['requested_attribute'] = {"min":0}
+ c_child_order.extend(['requested_attribute'])
+
+ def __init__(self,
+ requested_attribute=None,
+ text=None,
+ extension_elements=None,
+ extension_attributes=None,
+ ):
+ SamlBase.__init__(self,
+ text=text,
+ extension_elements=extension_elements,
+ extension_attributes=extension_attributes,
+ )
+ self.requested_attribute=requested_attribute or []
+
+def requested_attributes_type__from_string(xml_string):
+ return saml2.create_class_from_xml_string(RequestedAttributesType_, xml_string)
+
+
+class RequestedAttributes(RequestedAttributesType_):
+ """The http://eidas.europa.eu/saml-extensions:RequestedAttributes element """
+
+ c_tag = 'RequestedAttributes'
+ c_namespace = NAMESPACE
+ c_children = RequestedAttributesType_.c_children.copy()
+ c_attributes = RequestedAttributesType_.c_attributes.copy()
+ c_child_order = RequestedAttributesType_.c_child_order[:]
+ c_cardinality = RequestedAttributesType_.c_cardinality.copy()
+
+def requested_attributes_from_string(xml_string):
+ return saml2.create_class_from_xml_string(RequestedAttributes, xml_string)
+
+
+ELEMENT_FROM_STRING = {
+ RequestedAttributes.c_tag: requested_attributes_from_string,
+ RequestedAttributesType_.c_tag: requested_attributes_type__from_string,
+ RequestedAttribute.c_tag: requested_attribute_from_string,
+ RequestedAttributeType_.c_tag: requested_attribute_type__from_string,
+}
+
+ELEMENT_BY_TAG = {
+ 'RequestedAttributes': RequestedAttributes,
+ 'RequestedAttributesType': RequestedAttributesType_,
+ 'RequestedAttribute': RequestedAttribute,
+ 'RequestedAttributeType': RequestedAttributeType_,
+}
+
+
+def factory(tag, **kwargs):
+ return ELEMENT_BY_TAG[tag](**kwargs)
diff --git a/src/saml2/extension/sp_type.py b/src/saml2/extension/sp_type.py
new file mode 100644
index 00000000..8ffb2cea
--- /dev/null
+++ b/src/saml2/extension/sp_type.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+#
+# Generated Tue Jul 18 15:03:44 2017 by parse_xsd.py version 0.5.
+#
+
+import saml2
+from saml2 import SamlBase
+
+
+NAMESPACE = 'http://eidas.europa.eu/saml-extensions'
+
+class SPTypeType_(SamlBase):
+ """The http://eidas.europa.eu/saml-extensions:SPTypeType element """
+
+ c_tag = 'SPTypeType'
+ c_namespace = NAMESPACE
+ c_value_type = {'base': 'xsd:string', 'enumeration': ['public', 'private']}
+ c_children = SamlBase.c_children.copy()
+ c_attributes = SamlBase.c_attributes.copy()
+ c_child_order = SamlBase.c_child_order[:]
+ c_cardinality = SamlBase.c_cardinality.copy()
+
+def sp_type_type__from_string(xml_string):
+ return saml2.create_class_from_xml_string(SPTypeType_, xml_string)
+
+
+class SPType(SPTypeType_):
+ """The http://eidas.europa.eu/saml-extensions:SPType element """
+
+ c_tag = 'SPType'
+ c_namespace = NAMESPACE
+ c_children = SPTypeType_.c_children.copy()
+ c_attributes = SPTypeType_.c_attributes.copy()
+ c_child_order = SPTypeType_.c_child_order[:]
+ c_cardinality = SPTypeType_.c_cardinality.copy()
+
+def sp_type_from_string(xml_string):
+ return saml2.create_class_from_xml_string(SPType, xml_string)
+
+
+ELEMENT_FROM_STRING = {
+ SPType.c_tag: sp_type_from_string,
+ SPTypeType_.c_tag: sp_type_type__from_string,
+}
+
+ELEMENT_BY_TAG = {
+ 'SPType': SPType,
+ 'SPTypeType': SPTypeType_,
+}
+
+
+def factory(tag, **kwargs):
+ return ELEMENT_BY_TAG[tag](**kwargs)
diff --git a/src/saml2/metadata.py b/src/saml2/metadata.py
index 50ec0bae..de2e6e75 100644
--- a/src/saml2/metadata.py
+++ b/src/saml2/metadata.py
@@ -9,6 +9,7 @@ from saml2.extension import mdui
from saml2.extension import idpdisc
from saml2.extension import shibmd
from saml2.extension import mdattr
+from saml2.extension import sp_type
from saml2.saml import NAME_FORMAT_URI
from saml2.saml import AttributeValue
from saml2.saml import Attribute
@@ -722,7 +723,8 @@ def entity_descriptor(confd):
entd.contact_person = do_contact_person_info(confd.contact_person)
if confd.entity_category:
- entd.extensions = md.Extensions()
+ if not entd.extensions:
+ entd.extensions = md.Extensions()
ava = [AttributeValue(text=c) for c in confd.entity_category]
attr = Attribute(attribute_value=ava,
name="http://macedir.org/entity-category")
@@ -734,6 +736,14 @@ def entity_descriptor(confd):
entd.extensions = md.Extensions()
entd.extensions.add_extension_element(item)
+ conf_sp_type = confd.getattr('sp_type', 'sp')
+ conf_sp_type_in_md = confd.getattr('sp_type_in_metadata', 'sp')
+ if conf_sp_type and conf_sp_type_in_md is True:
+ if not entd.extensions:
+ entd.extensions = md.Extensions()
+ item = sp_type.SPType(text=conf_sp_type)
+ entd.extensions.add_extension_element(item)
+
serves = confd.serves
if not serves:
raise SAMLError(