diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/saml2/attributemaps/saml_uri.py | 17 | ||||
-rw-r--r-- | src/saml2/client_base.py | 66 | ||||
-rw-r--r-- | src/saml2/config.py | 3 | ||||
-rw-r--r-- | src/saml2/extension/requested_attributes.py | 131 | ||||
-rw-r--r-- | src/saml2/extension/sp_type.py | 54 | ||||
-rw-r--r-- | src/saml2/metadata.py | 12 |
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( |