diff --git a/doc/conf.py b/doc/conf.py index 592e103..8397b3d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -45,9 +45,9 @@ copyright = u'2010-2011, Roland Hedberg' # built documents. # # The short X.Y version. -version = '0.4' +version = '1.2' # The full version, including alpha/beta/rc tags. -release = '0.4.2' +release = '1.2.0beta' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/install.rst b/doc/install.rst index 5f13583..58910e2 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -10,8 +10,8 @@ Install PySAML2 --------------- For all this to work you need to have Python installed. -The development has been done using 2.6. -There is no 3.X version yet. +The development has been done using 2.7. +There is now a 3.X version. Prerequisites ^^^^^^^^^^^^^ @@ -21,19 +21,17 @@ if it's recent enough, or if the Python is too old you have to install it, for instance by getting it from the Python Package Instance by using easy_install. -You also need xmlsec which you can download from http://www.aleksey.com/xmlsec/ +You also need xmlsec1 which you can download from http://www.aleksey.com/xmlsec/ -If you're on OS X you can get xmlsec installed from MacPorts or Fink. +If you're on OS X you can get xmlsec1 installed from MacPorts or Fink. Depending on how you are going to use PySAML2 you might also need * Mako * pyASN1 -* repoze.who (make sure you get 1.0.16 and not 2.0) -* decorator +* repoze.who * python-memcache * memcached -* M2Crypto Quick build instructions ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,6 +42,10 @@ Once you have installed all the necessary prerequisites a simple:: will install the basic code. +Note for rhel/centos 6: cffi depends on libffi-devel, and cryptography on openssl-devel to compile +So you might want first to do: +yum install libffi-devel openssl-devel + After this you ought to be able to run the tests without an hitch. The tests are based on the pypy test environment, so:: diff --git a/setup.py b/setup.py index 468ff97..26bbbc7 100755 --- a/setup.py +++ b/setup.py @@ -45,7 +45,8 @@ install_requires = [ 'repoze.who', 'pycrypto', # 'Crypto' 'pytz', - 'pyOpenSSL' + 'pyOpenSSL', + 'python-dateutil' ] tests_require = [ diff --git a/src/saml2/__init__.py b/src/saml2/__init__.py index 38631e4..8ea0a0c 100644 --- a/src/saml2/__init__.py +++ b/src/saml2/__init__.py @@ -444,13 +444,12 @@ class SamlBase(ExtensionContainer): c_children = {} c_attributes = {} c_attribute_type = {} - #c_attribute_use = {} - #c_attribute_required = {} c_child_order = [] c_cardinality = {} c_any = None c_any_attribute = None c_value_type = None + c_ns_prefix = None def _get_all_c_children_with_order(self): if len(self.c_child_order) > 0: @@ -549,6 +548,9 @@ class SamlBase(ExtensionContainer): constructing the text representation. :return: String representation of the object """ + if not nspair and self.c_ns_prefix: + nspair = self.c_ns_prefix + if nspair: for prefix, uri in nspair.items(): try: diff --git a/src/saml2/attributemaps/saml_uri.py b/src/saml2/attributemaps/saml_uri.py index ef1921e..877ffc6 100644 --- a/src/saml2/attributemaps/saml_uri.py +++ b/src/saml2/attributemaps/saml_uri.py @@ -7,7 +7,7 @@ NETSCAPE_LDAP = "urn:oid:2.16.840.1.113730.3.1." UCL_DIR_PILOT = 'urn:oid:0.9.2342.19200300.100.1.' PKCS_9 = "urn:oid:1.2.840.113549.1.9.1." UMICH = "urn:oid:1.3.6.1.4.1.250.1.57." -SCHAC = "urn:oid:1.3.6.1.4.1.25178.2." +SCHAC = "urn:oid:1.3.6.1.4.1.25178.1.2." #urn:oid:1.3.6.1.4.1.1466.115.121.1.26 diff --git a/src/saml2/client.py b/src/saml2/client.py index 4bdc146..9e98671 100644 --- a/src/saml2/client.py +++ b/src/saml2/client.py @@ -254,12 +254,6 @@ class Saml2Client(Base): status["reason"], status["not_on_or_after"], status["sign"]) - # ======================================================================== - # MUST use SOAP for - # AssertionIDRequest, SubjectQuery, AuthnQuery, AttributeQuery or - # AuthzDecisionQuery - # ======================================================================== - def _use_soap(self, destination, query_type, **kwargs): _create_func = getattr(self, "create_%s" % query_type) _response_func = getattr(self, "parse_%s_response" % query_type) @@ -432,13 +426,14 @@ class Saml2Client(Base): :param sign: Whether the response will be signed or not :return: Keyword arguments which can be used to send the response what's returned follow different patterns for different bindings. - If the binding is BINDIND_SOAP, what is returned looks like this: - { - "data": - "url": "", - 'headers': [('content-type', 'application/soap+xml')] - 'method': "POST - } + If the binding is BINDIND_SOAP, what is returned looks like this:: + + { + "data": + "url": "", + 'headers': [('content-type', 'application/soap+xml')] + 'method': "POST + } """ logger.info("logout request: %s" % request) diff --git a/src/saml2/config.py b/src/saml2/config.py index d31cd77..146fef1 100644 --- a/src/saml2/config.py +++ b/src/saml2/config.py @@ -70,7 +70,8 @@ COMMON_ARGS = [ "verify_encrypt_cert", "tmp_cert_file", "tmp_key_file", - "validate_certificate" + "validate_certificate", + "extensions" ] SP_ARGS = [ @@ -219,6 +220,7 @@ class Config(object): self.tmp_cert_file = None self.tmp_key_file = None self.validate_certificate = None + self.extensions = {} def setattr(self, context, attr, val): if context == "": @@ -337,6 +339,9 @@ class Config(object): except KeyError: pass + if "extensions" in cnf: + self.do_extensions(cnf["extensions"]) + self.load_complex(cnf, metadata_construction=metadata_construction) self.context = self.def_context @@ -480,6 +485,11 @@ class Config(object): return None, None + def do_extensions(self, extensions): + for key, val in extensions.items(): + self.extensions[key] = val + + class SPConfig(Config): def_context = "sp" diff --git a/src/saml2/entity_category/incommon.py b/src/saml2/entity_category/incommon.py index 2956ff2..e1c1462 100644 --- a/src/saml2/entity_category/incommon.py +++ b/src/saml2/entity_category/incommon.py @@ -1,10 +1,11 @@ __author__ = 'rolandh' -INC = "http://id.incommon.org/category/research-and-scholarship" +RESEARCH_AND_SCHOLARSHIP = "http://id.incommon.org/category/research-and-scholarship" RELEASE = { "": ["eduPersonTargetedID"], - INC: ["eduPersonPrincipalName", "eduPersonScopedAffiliation", "mail", - "givenName", "sn", "displayName"] + RESEARCH_AND_SCHOLARSHIP: ["eduPersonPrincipalName", + "eduPersonScopedAffiliation", "mail", + "givenName", "sn", "displayName"] } diff --git a/src/saml2/entity_category/refeds.py b/src/saml2/entity_category/refeds.py new file mode 100644 index 0000000..c6fe882 --- /dev/null +++ b/src/saml2/entity_category/refeds.py @@ -0,0 +1,11 @@ +__author__ = 'rolandh' + +RESEARCH_AND_SCHOLARSHIP = "http://refeds.org/category/research-and-scholarship" + +RELEASE = { + "": ["eduPersonTargetedID"], + RESEARCH_AND_SCHOLARSHIP: ["eduPersonPrincipalName", + "eduPersonScopedAffiliation", "mail", + "givenName", "sn", "displayName"] +} + diff --git a/src/saml2/entity_category/swamid.py b/src/saml2/entity_category/swamid.py index 7d07f0f..94ebe62 100644 --- a/src/saml2/entity_category/swamid.py +++ b/src/saml2/entity_category/swamid.py @@ -2,7 +2,7 @@ __author__ = 'rolandh' NAME = ["givenName", "displayName", "sn"] -STATIC_ORG_INFO = ["c", "o", "co"] +STATIC_ORG_INFO = ["c", "o", "co", "norEduOrgAcronym", "schacHomeOrganization"] OTHER = ["eduPersonPrincipalName", "eduPersonScopedAffiliation", "mail"] # These give you access to information diff --git a/src/saml2/extension/mdrpi.py b/src/saml2/extension/mdrpi.py index 76e6e06..5951ed6 100644 --- a/src/saml2/extension/mdrpi.py +++ b/src/saml2/extension/mdrpi.py @@ -11,6 +11,7 @@ from saml2 import md NAMESPACE = 'urn:oasis:names:tc:SAML:metadata:rpi' + class RegistrationPolicy(md.LocalizedURIType_): """The urn:oasis:names:tc:SAML:metadata:rpi:RegistrationPolicy element """ @@ -21,6 +22,7 @@ class RegistrationPolicy(md.LocalizedURIType_): c_child_order = md.LocalizedURIType_.c_child_order[:] c_cardinality = md.LocalizedURIType_.c_cardinality.copy() + def registration_policy_from_string(xml_string): return saml2.create_class_from_xml_string(RegistrationPolicy, xml_string) @@ -35,6 +37,7 @@ class UsagePolicy(md.LocalizedURIType_): c_child_order = md.LocalizedURIType_.c_child_order[:] c_cardinality = md.LocalizedURIType_.c_cardinality.copy() + def usage_policy_from_string(xml_string): return saml2.create_class_from_xml_string(UsagePolicy, xml_string) @@ -53,21 +56,20 @@ class PublicationType_(SamlBase): c_attributes['publicationId'] = ('publication_id', 'string', False) def __init__(self, - publisher=None, - creation_instant=None, - publication_id=None, - text=None, - extension_elements=None, - extension_attributes=None - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.publisher=publisher - self.creation_instant=creation_instant - self.publication_id=publication_id + publisher=None, + creation_instant=None, + publication_id=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.publisher = publisher + self.creation_instant = creation_instant + self.publication_id = publication_id + def publication_type__from_string(xml_string): return saml2.create_class_from_xml_string(PublicationType_, xml_string) @@ -82,28 +84,30 @@ class RegistrationInfoType_(SamlBase): 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:metadata:rpi}RegistrationPolicy'] = ('registration_policy', [RegistrationPolicy]) - c_cardinality['registration_policy'] = {"min":0} - c_attributes['registrationAuthority'] = ('registration_authority', 'string', True) - c_attributes['registrationInstant'] = ('registration_instant', 'dateTime', False) + c_children['{urn:oasis:names:tc:SAML:metadata:rpi}RegistrationPolicy'] = ( + 'registration_policy', [RegistrationPolicy]) + c_cardinality['registration_policy'] = {"min": 0} + c_attributes['registrationAuthority'] = ( + 'registration_authority', 'string', True) + c_attributes['registrationInstant'] = ( + 'registration_instant', 'dateTime', False) c_child_order.extend(['registration_policy']) def __init__(self, - registration_policy=None, - registration_authority=None, - registration_instant=None, - text=None, - extension_elements=None, - extension_attributes=None - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.registration_policy=registration_policy or [] - self.registration_authority=registration_authority - self.registration_instant=registration_instant + registration_policy=None, + registration_authority=None, + registration_instant=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.registration_policy = registration_policy or [] + self.registration_authority = registration_authority + self.registration_instant = registration_instant + def registration_info_type__from_string(xml_string): return saml2.create_class_from_xml_string(RegistrationInfoType_, xml_string) @@ -118,31 +122,31 @@ class PublicationInfoType_(SamlBase): 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:metadata:rpi}UsagePolicy'] = ('usage_policy', [UsagePolicy]) - c_cardinality['usage_policy'] = {"min":0} + c_children['{urn:oasis:names:tc:SAML:metadata:rpi}UsagePolicy'] = ( + 'usage_policy', [UsagePolicy]) + c_cardinality['usage_policy'] = {"min": 0} c_attributes['publisher'] = ('publisher', 'string', True) c_attributes['creationInstant'] = ('creation_instant', 'dateTime', False) c_attributes['publicationId'] = ('publication_id', 'string', False) c_child_order.extend(['usage_policy']) def __init__(self, - usage_policy=None, - publisher=None, - creation_instant=None, - publication_id=None, - text=None, - extension_elements=None, - extension_attributes=None - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.usage_policy=usage_policy or [] - self.publisher=publisher - self.creation_instant=creation_instant - self.publication_id=publication_id + usage_policy=None, + publisher=None, + creation_instant=None, + publication_id=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.usage_policy = usage_policy or [] + self.publisher = publisher + self.creation_instant = creation_instant + self.publication_id = publication_id + def publication_info_type__from_string(xml_string): return saml2.create_class_from_xml_string(PublicationInfoType_, xml_string) @@ -158,6 +162,7 @@ class Publication(PublicationType_): c_child_order = PublicationType_.c_child_order[:] c_cardinality = PublicationType_.c_cardinality.copy() + def publication_from_string(xml_string): return saml2.create_class_from_xml_string(Publication, xml_string) @@ -172,6 +177,7 @@ class RegistrationInfo(RegistrationInfoType_): c_child_order = RegistrationInfoType_.c_child_order[:] c_cardinality = RegistrationInfoType_.c_cardinality.copy() + def registration_info_from_string(xml_string): return saml2.create_class_from_xml_string(RegistrationInfo, xml_string) @@ -186,6 +192,7 @@ class PublicationInfo(PublicationInfoType_): c_child_order = PublicationInfoType_.c_child_order[:] c_cardinality = PublicationInfoType_.c_cardinality.copy() + def publication_info_from_string(xml_string): return saml2.create_class_from_xml_string(PublicationInfo, xml_string) @@ -199,22 +206,22 @@ class PublicationPathType_(SamlBase): 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:metadata:rpi}Publication'] = ('publication', [Publication]) - c_cardinality['publication'] = {"min":0} + c_children['{urn:oasis:names:tc:SAML:metadata:rpi}Publication'] = ( + 'publication', [Publication]) + c_cardinality['publication'] = {"min": 0} c_child_order.extend(['publication']) def __init__(self, - publication=None, - text=None, - extension_elements=None, - extension_attributes=None - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.publication=publication or [] + publication=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.publication = publication or [] + def publication_path_type__from_string(xml_string): return saml2.create_class_from_xml_string(PublicationPathType_, xml_string) @@ -230,6 +237,7 @@ class PublicationPath(PublicationPathType_): c_child_order = PublicationPathType_.c_child_order[:] c_cardinality = PublicationPathType_.c_cardinality.copy() + def publication_path_from_string(xml_string): return saml2.create_class_from_xml_string(PublicationPath, xml_string) diff --git a/src/saml2/extension/mdui.py b/src/saml2/extension/mdui.py index 5dcd319..b27933c 100644 --- a/src/saml2/extension/mdui.py +++ b/src/saml2/extension/mdui.py @@ -11,6 +11,7 @@ from saml2 import md NAMESPACE = 'urn:oasis:names:tc:SAML:metadata:ui' + class DisplayName(md.LocalizedNameType_): """The urn:oasis:names:tc:SAML:metadata:ui:DisplayName element """ @@ -21,6 +22,7 @@ class DisplayName(md.LocalizedNameType_): c_child_order = md.LocalizedNameType_.c_child_order[:] c_cardinality = md.LocalizedNameType_.c_cardinality.copy() + def display_name_from_string(xml_string): return saml2.create_class_from_xml_string(DisplayName, xml_string) @@ -35,6 +37,7 @@ class Description(md.LocalizedNameType_): c_child_order = md.LocalizedNameType_.c_child_order[:] c_cardinality = md.LocalizedNameType_.c_cardinality.copy() + def description_from_string(xml_string): return saml2.create_class_from_xml_string(Description, xml_string) @@ -49,6 +52,7 @@ class InformationURL(md.LocalizedURIType_): c_child_order = md.LocalizedURIType_.c_child_order[:] c_cardinality = md.LocalizedURIType_.c_cardinality.copy() + def information_url_from_string(xml_string): return saml2.create_class_from_xml_string(InformationURL, xml_string) @@ -63,6 +67,7 @@ class PrivacyStatementURL(md.LocalizedURIType_): c_child_order = md.LocalizedURIType_.c_child_order[:] c_cardinality = md.LocalizedURIType_.c_cardinality.copy() + def privacy_statement_url_from_string(xml_string): return saml2.create_class_from_xml_string(PrivacyStatementURL, xml_string) @@ -78,6 +83,7 @@ class ListOfStrings_(SamlBase): c_child_order = SamlBase.c_child_order[:] c_cardinality = SamlBase.c_cardinality.copy() + def list_of_strings__from_string(xml_string): return saml2.create_class_from_xml_string(ListOfStrings_, xml_string) @@ -91,20 +97,20 @@ class KeywordsType_(ListOfStrings_): c_attributes = ListOfStrings_.c_attributes.copy() c_child_order = ListOfStrings_.c_child_order[:] c_cardinality = ListOfStrings_.c_cardinality.copy() - c_attributes['{http://www.w3.org/XML/1998/namespace}lang'] = ('lang', 'mdui:listOfStrings', True) + c_attributes['{http://www.w3.org/XML/1998/namespace}lang'] = ( + 'lang', 'mdui:listOfStrings', True) def __init__(self, - lang=None, - text=None, - extension_elements=None, - extension_attributes=None, - ): - ListOfStrings_.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.lang=lang + lang=None, + text=None, + extension_elements=None, + extension_attributes=None): + ListOfStrings_.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.lang = lang + def keywords_type__from_string(xml_string): return saml2.create_class_from_xml_string(KeywordsType_, xml_string) @@ -122,24 +128,24 @@ class LogoType_(SamlBase): c_cardinality = SamlBase.c_cardinality.copy() c_attributes['height'] = ('height', 'positiveInteger', True) c_attributes['width'] = ('width', 'positiveInteger', True) - c_attributes['{http://www.w3.org/XML/1998/namespace}lang'] = ('lang', 'anyURI', False) + c_attributes['{http://www.w3.org/XML/1998/namespace}lang'] = ( + 'lang', 'anyURI', False) def __init__(self, - height=None, - width=None, - lang=None, - text=None, - extension_elements=None, - extension_attributes=None, - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.height=height - self.width=width - self.lang=lang + height=None, + width=None, + lang=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.height = height + self.width = width + self.lang = lang + def logo_type__from_string(xml_string): return saml2.create_class_from_xml_string(LogoType_, xml_string) @@ -156,6 +162,7 @@ class IPHint(SamlBase): c_child_order = SamlBase.c_child_order[:] c_cardinality = SamlBase.c_cardinality.copy() + def ip_hint_from_string(xml_string): return saml2.create_class_from_xml_string(IPHint, xml_string) @@ -171,6 +178,7 @@ class DomainHint(SamlBase): c_child_order = SamlBase.c_child_order[:] c_cardinality = SamlBase.c_cardinality.copy() + def domain_hint_from_string(xml_string): return saml2.create_class_from_xml_string(DomainHint, xml_string) @@ -186,6 +194,7 @@ class GeolocationHint(SamlBase): c_child_order = SamlBase.c_child_order[:] c_cardinality = SamlBase.c_cardinality.copy() + def geolocation_hint_from_string(xml_string): return saml2.create_class_from_xml_string(GeolocationHint, xml_string) @@ -200,6 +209,7 @@ class Keywords(KeywordsType_): c_child_order = KeywordsType_.c_child_order[:] c_cardinality = KeywordsType_.c_cardinality.copy() + def keywords_from_string(xml_string): return saml2.create_class_from_xml_string(Keywords, xml_string) @@ -214,6 +224,7 @@ class Logo(LogoType_): c_child_order = LogoType_.c_child_order[:] c_cardinality = LogoType_.c_cardinality.copy() + def logo_from_string(xml_string): return saml2.create_class_from_xml_string(Logo, xml_string) @@ -227,30 +238,32 @@ class DiscoHintsType_(SamlBase): 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:metadata:ui}IPHint'] = ('ip_hint', [IPHint]) - c_cardinality['ip_hint'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}DomainHint'] = ('domain_hint', [DomainHint]) - c_cardinality['domain_hint'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}GeolocationHint'] = ('geolocation_hint', [GeolocationHint]) - c_cardinality['geolocation_hint'] = {"min":0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}IPHint'] = ( + 'ip_hint', [IPHint]) + c_cardinality['ip_hint'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}DomainHint'] = ( + 'domain_hint', [DomainHint]) + c_cardinality['domain_hint'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}GeolocationHint'] = ( + 'geolocation_hint', [GeolocationHint]) + c_cardinality['geolocation_hint'] = {"min": 0} c_child_order.extend(['ip_hint', 'domain_hint', 'geolocation_hint']) def __init__(self, - ip_hint=None, - domain_hint=None, - geolocation_hint=None, - text=None, - extension_elements=None, - extension_attributes=None, - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.ip_hint=ip_hint or [] - self.domain_hint=domain_hint or [] - self.geolocation_hint=geolocation_hint or [] + ip_hint=None, + domain_hint=None, + geolocation_hint=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.ip_hint = ip_hint or [] + self.domain_hint = domain_hint or [] + self.geolocation_hint = geolocation_hint or [] + def disco_hints_type__from_string(xml_string): return saml2.create_class_from_xml_string(DiscoHintsType_, xml_string) @@ -265,42 +278,48 @@ class UIInfoType_(SamlBase): 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:metadata:ui}DisplayName'] = ('display_name', [DisplayName]) - c_cardinality['display_name'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}Description'] = ('description', [Description]) - c_cardinality['description'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}Keywords'] = ('keywords', [Keywords]) - c_cardinality['keywords'] = {"min":0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}DisplayName'] = ( + 'display_name', [DisplayName]) + c_cardinality['display_name'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}Description'] = ( + 'description', [Description]) + c_cardinality['description'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}Keywords'] = ( + 'keywords', [Keywords]) + c_cardinality['keywords'] = {"min": 0} c_children['{urn:oasis:names:tc:SAML:metadata:ui}Logo'] = ('logo', [Logo]) - c_cardinality['logo'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}InformationURL'] = ('information_url', [InformationURL]) - c_cardinality['information_url'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}PrivacyStatementURL'] = ('privacy_statement_url', [PrivacyStatementURL]) - c_cardinality['privacy_statement_url'] = {"min":0} - c_child_order.extend(['display_name', 'description', 'keywords', 'logo', 'information_url', 'privacy_statement_url']) + c_cardinality['logo'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}InformationURL'] = ( + 'information_url', [InformationURL]) + c_cardinality['information_url'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}PrivacyStatementURL'] = ( + 'privacy_statement_url', [PrivacyStatementURL]) + c_cardinality['privacy_statement_url'] = {"min": 0} + c_child_order.extend( + ['display_name', 'description', 'keywords', 'logo', 'information_url', + 'privacy_statement_url']) def __init__(self, - display_name=None, - description=None, - keywords=None, - logo=None, - information_url=None, - privacy_statement_url=None, - text=None, - extension_elements=None, - extension_attributes=None, - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.display_name=display_name or [] - self.description=description or [] - self.keywords=keywords or [] - self.logo=logo or [] - self.information_url=information_url or [] - self.privacy_statement_url=privacy_statement_url or [] + display_name=None, + description=None, + keywords=None, + logo=None, + information_url=None, + privacy_statement_url=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.display_name = display_name or [] + self.description = description or [] + self.keywords = keywords or [] + self.logo = logo or [] + self.information_url = information_url or [] + self.privacy_statement_url = privacy_statement_url or [] + def ui_info_type__from_string(xml_string): return saml2.create_class_from_xml_string(UIInfoType_, xml_string) @@ -316,6 +335,7 @@ class DiscoHints(DiscoHintsType_): c_child_order = DiscoHintsType_.c_child_order[:] c_cardinality = DiscoHintsType_.c_cardinality.copy() + def disco_hints_from_string(xml_string): return saml2.create_class_from_xml_string(DiscoHints, xml_string) @@ -330,6 +350,7 @@ class UIInfo(UIInfoType_): c_child_order = UIInfoType_.c_child_order[:] c_cardinality = UIInfoType_.c_cardinality.copy() + def ui_info_from_string(xml_string): return saml2.create_class_from_xml_string(UIInfo, xml_string) @@ -375,4 +396,3 @@ ELEMENT_BY_TAG = { def factory(tag, **kwargs): return ELEMENT_BY_TAG[tag](**kwargs) - diff --git a/src/saml2/extension/shibmd.py b/src/saml2/extension/shibmd.py index 4da9976..0613f56 100644 --- a/src/saml2/extension/shibmd.py +++ b/src/saml2/extension/shibmd.py @@ -11,6 +11,7 @@ import xmldsig as ds NAMESPACE = 'urn:mace:shibboleth:metadata:1.0' + class Scope(SamlBase): """The urn:mace:shibboleth:metadata:1.0:Scope element """ @@ -24,17 +25,16 @@ class Scope(SamlBase): c_attributes['regexp'] = ('regexp', 'boolean', False) def __init__(self, - regexp='false', - text=None, - extension_elements=None, - extension_attributes=None, - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.regexp=regexp + regexp='false', + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.regexp = regexp + def scope_from_string(xml_string): return saml2.create_class_from_xml_string(Scope, xml_string) @@ -49,25 +49,25 @@ class KeyAuthority(SamlBase): c_attributes = SamlBase.c_attributes.copy() c_child_order = SamlBase.c_child_order[:] c_cardinality = SamlBase.c_cardinality.copy() - c_children['{http://www.w3.org/2000/09/xmldsig#}KeyInfo'] = ('key_info', [ds.KeyInfo]) - c_cardinality['key_info'] = {"min":1} + c_children['{http://www.w3.org/2000/09/xmldsig#}KeyInfo'] = ('key_info', + [ds.KeyInfo]) + c_cardinality['key_info'] = {"min": 1} c_attributes['VerifyDepth'] = ('verify_depth', 'unsignedByte', False) c_child_order.extend(['key_info']) def __init__(self, - key_info=None, - verify_depth='1', - text=None, - extension_elements=None, - extension_attributes=None, - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.key_info=key_info or [] - self.verify_depth=verify_depth + key_info=None, + verify_depth='1', + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.key_info = key_info or [] + self.verify_depth = verify_depth + def key_authority_from_string(xml_string): return saml2.create_class_from_xml_string(KeyAuthority, xml_string) @@ -86,4 +86,3 @@ ELEMENT_BY_TAG = { def factory(tag, **kwargs): return ELEMENT_BY_TAG[tag](**kwargs) - diff --git a/src/saml2/extension/ui.py b/src/saml2/extension/ui.py index 9fbaaee..aff4f75 100644 --- a/src/saml2/extension/ui.py +++ b/src/saml2/extension/ui.py @@ -11,6 +11,7 @@ from saml2 import md NAMESPACE = 'urn:oasis:names:tc:SAML:metadata:ui' + class DisplayName(md.LocalizedNameType_): """The urn:oasis:names:tc:SAML:metadata:ui:DisplayName element """ @@ -21,6 +22,7 @@ class DisplayName(md.LocalizedNameType_): c_child_order = md.LocalizedNameType_.c_child_order[:] c_cardinality = md.LocalizedNameType_.c_cardinality.copy() + def display_name_from_string(xml_string): return saml2.create_class_from_xml_string(DisplayName, xml_string) @@ -35,6 +37,7 @@ class Description(md.LocalizedNameType_): c_child_order = md.LocalizedNameType_.c_child_order[:] c_cardinality = md.LocalizedNameType_.c_cardinality.copy() + def description_from_string(xml_string): return saml2.create_class_from_xml_string(Description, xml_string) @@ -49,6 +52,7 @@ class InformationURL(md.LocalizedURIType_): c_child_order = md.LocalizedURIType_.c_child_order[:] c_cardinality = md.LocalizedURIType_.c_cardinality.copy() + def information_url_from_string(xml_string): return saml2.create_class_from_xml_string(InformationURL, xml_string) @@ -63,6 +67,7 @@ class PrivacyStatementURL(md.LocalizedURIType_): c_child_order = md.LocalizedURIType_.c_child_order[:] c_cardinality = md.LocalizedURIType_.c_cardinality.copy() + def privacy_statement_url_from_string(xml_string): return saml2.create_class_from_xml_string(PrivacyStatementURL, xml_string) @@ -79,24 +84,24 @@ class LogoType_(SamlBase): c_cardinality = SamlBase.c_cardinality.copy() c_attributes['height'] = ('height', 'positiveInteger', True) c_attributes['width'] = ('width', 'positiveInteger', True) - c_attributes['{http://www.w3.org/XML/1998/namespace}lang'] = ('lang', 'anyURI', False) + c_attributes['{http://www.w3.org/XML/1998/namespace}lang'] = ( + 'lang', 'anyURI', False) def __init__(self, - height=None, - width=None, - lang=None, - text=None, - extension_elements=None, - extension_attributes=None, - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.height=height - self.width=width - self.lang=lang + height=None, + width=None, + lang=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.height = height + self.width = width + self.lang = lang + def logo_type__from_string(xml_string): return saml2.create_class_from_xml_string(LogoType_, xml_string) @@ -113,6 +118,7 @@ class IPHint(SamlBase): c_child_order = SamlBase.c_child_order[:] c_cardinality = SamlBase.c_cardinality.copy() + def ip_hint_from_string(xml_string): return saml2.create_class_from_xml_string(IPHint, xml_string) @@ -128,6 +134,7 @@ class DomainHint(SamlBase): c_child_order = SamlBase.c_child_order[:] c_cardinality = SamlBase.c_cardinality.copy() + def domain_hint_from_string(xml_string): return saml2.create_class_from_xml_string(DomainHint, xml_string) @@ -143,6 +150,7 @@ class GeolocationHint(SamlBase): c_child_order = SamlBase.c_child_order[:] c_cardinality = SamlBase.c_cardinality.copy() + def geolocation_hint_from_string(xml_string): return saml2.create_class_from_xml_string(GeolocationHint, xml_string) @@ -157,6 +165,7 @@ class Logo(LogoType_): c_child_order = LogoType_.c_child_order[:] c_cardinality = LogoType_.c_cardinality.copy() + def logo_from_string(xml_string): return saml2.create_class_from_xml_string(Logo, xml_string) @@ -170,30 +179,32 @@ class DiscoHintsType_(SamlBase): 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:metadata:ui}IPHint'] = ('ip_hint', [IPHint]) - c_cardinality['ip_hint'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}DomainHint'] = ('domain_hint', [DomainHint]) - c_cardinality['domain_hint'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}GeolocationHint'] = ('geolocation_hint', [GeolocationHint]) - c_cardinality['geolocation_hint'] = {"min":0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}IPHint'] = ( + 'ip_hint', [IPHint]) + c_cardinality['ip_hint'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}DomainHint'] = ( + 'domain_hint', [DomainHint]) + c_cardinality['domain_hint'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}GeolocationHint'] = ( + 'geolocation_hint', [GeolocationHint]) + c_cardinality['geolocation_hint'] = {"min": 0} c_child_order.extend(['ip_hint', 'domain_hint', 'geolocation_hint']) def __init__(self, - ip_hint=None, - domain_hint=None, - geolocation_hint=None, - text=None, - extension_elements=None, - extension_attributes=None, - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.ip_hint=ip_hint or [] - self.domain_hint=domain_hint or [] - self.geolocation_hint=geolocation_hint or [] + ip_hint=None, + domain_hint=None, + geolocation_hint=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.ip_hint = ip_hint or [] + self.domain_hint = domain_hint or [] + self.geolocation_hint = geolocation_hint or [] + def disco_hints_type__from_string(xml_string): return saml2.create_class_from_xml_string(DiscoHintsType_, xml_string) @@ -208,38 +219,43 @@ class UIInfoType_(SamlBase): 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:metadata:ui}DisplayName'] = ('display_name', [DisplayName]) - c_cardinality['display_name'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}Description'] = ('description', [Description]) - c_cardinality['description'] = {"min":0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}DisplayName'] = ( + 'display_name', [DisplayName]) + c_cardinality['display_name'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}Description'] = ( + 'description', [Description]) + c_cardinality['description'] = {"min": 0} c_children['{urn:oasis:names:tc:SAML:metadata:ui}Logo'] = ('logo', [Logo]) - c_cardinality['logo'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}InformationURL'] = ('information_url', [InformationURL]) - c_cardinality['information_url'] = {"min":0} - c_children['{urn:oasis:names:tc:SAML:metadata:ui}PrivacyStatementURL'] = ('privacy_statement_url', [PrivacyStatementURL]) - c_cardinality['privacy_statement_url'] = {"min":0} - c_child_order.extend(['display_name', 'description', 'logo', 'information_url', 'privacy_statement_url']) + c_cardinality['logo'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}InformationURL'] = ( + 'information_url', [InformationURL]) + c_cardinality['information_url'] = {"min": 0} + c_children['{urn:oasis:names:tc:SAML:metadata:ui}PrivacyStatementURL'] = ( + 'privacy_statement_url', [PrivacyStatementURL]) + c_cardinality['privacy_statement_url'] = {"min": 0} + c_child_order.extend( + ['display_name', 'description', 'logo', 'information_url', + 'privacy_statement_url']) def __init__(self, - display_name=None, - description=None, - logo=None, - information_url=None, - privacy_statement_url=None, - text=None, - extension_elements=None, - extension_attributes=None, - ): - SamlBase.__init__(self, - text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - ) - self.display_name=display_name or [] - self.description=description or [] - self.logo=logo or [] - self.information_url=information_url or [] - self.privacy_statement_url=privacy_statement_url or [] + display_name=None, + description=None, + logo=None, + information_url=None, + privacy_statement_url=None, + text=None, + extension_elements=None, + extension_attributes=None): + SamlBase.__init__(self, + text=text, + extension_elements=extension_elements, + extension_attributes=extension_attributes) + self.display_name = display_name or [] + self.description = description or [] + self.logo = logo or [] + self.information_url = information_url or [] + self.privacy_statement_url = privacy_statement_url or [] + def ui_info_type__from_string(xml_string): return saml2.create_class_from_xml_string(UIInfoType_, xml_string) @@ -255,6 +271,7 @@ class DiscoHints(DiscoHintsType_): c_child_order = DiscoHintsType_.c_child_order[:] c_cardinality = DiscoHintsType_.c_cardinality.copy() + def disco_hints_from_string(xml_string): return saml2.create_class_from_xml_string(DiscoHints, xml_string) @@ -269,6 +286,7 @@ class UIInfo(UIInfoType_): c_child_order = UIInfoType_.c_child_order[:] c_cardinality = UIInfoType_.c_cardinality.copy() + def ui_info_from_string(xml_string): return saml2.create_class_from_xml_string(UIInfo, xml_string) diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py index bbde35f..0a7726e 100644 --- a/src/saml2/mdstore.py +++ b/src/saml2/mdstore.py @@ -56,6 +56,8 @@ REQ2SRV = { ENTITYATTRIBUTES = "urn:oasis:names:tc:SAML:metadata:attribute&EntityAttributes" +ENTITY_CATEGORY = "http://macedir.org/entity-category" +ENTITY_CATEGORY_SUPPORT = "http://macedir.org/entity-category-support" # --------------------------------------------------- @@ -598,14 +600,14 @@ class MetadataStore(object): if binding is None: binding = BINDING_HTTP_REDIRECT return self.service(entity_id, "idpsso_descriptor", - "single_sign_on_service", binding) + "single_sign_on_service", binding) def name_id_mapping_service(self, entity_id, binding=None, typ="idpsso"): # IDP if binding is None: binding = BINDING_HTTP_REDIRECT return self.service(entity_id, "idpsso_descriptor", - "name_id_mapping_service", binding) + "name_id_mapping_service", binding) def authn_query_service(self, entity_id, binding=None, typ="authn_authority"): @@ -613,7 +615,7 @@ class MetadataStore(object): if binding is None: binding = BINDING_SOAP return self.service(entity_id, "authn_authority_descriptor", - "authn_query_service", binding) + "authn_query_service", binding) def attribute_service(self, entity_id, binding=None, typ="attribute_authority"): @@ -621,7 +623,7 @@ class MetadataStore(object): if binding is None: binding = BINDING_HTTP_REDIRECT return self.service(entity_id, "attribute_authority_descriptor", - "attribute_service", binding) + "attribute_service", binding) def authz_service(self, entity_id, binding=None, typ="pdp"): # PDP @@ -773,13 +775,36 @@ class MetadataStore(object): return [m["text"] for m in ad["affiliate_member"]] def entity_categories(self, entity_id): - ext = self.__getitem__(entity_id)["extensions"] + ent = self.__getitem__(entity_id) res = [] - for elem in ext["extension_elements"]: - if elem["__class__"] == ENTITYATTRIBUTES: - for attr in elem["attribute"]: - if attr["name"] == "http://macedir.org/entity-category": - res.extend([v["text"] for v in attr["attribute_value"]]) + try: + ext = ent["extensions"] + except KeyError: + pass + else: + for elem in ext["extension_elements"]: + if elem["__class__"] == ENTITYATTRIBUTES: + for attr in elem["attribute"]: + if attr["name"] == ENTITY_CATEGORY: + res.extend([v["text"] for v in + attr["attribute_value"]]) + + return res + + def supported_entity_categories(self, entity_id): + ent = self.__getitem__(entity_id) + res = [] + try: + ext = ent["extensions"] + except KeyError: + pass + else: + for elem in ext["extension_elements"]: + if elem["__class__"] == ENTITYATTRIBUTES: + for attr in elem["attribute"]: + if attr["name"] == ENTITY_CATEGORY_SUPPORT: + res.extend([v["text"] for v in + attr["attribute_value"]]) return res diff --git a/src/saml2/metadata.py b/src/saml2/metadata.py index acdeac8..afeb701 100644 --- a/src/saml2/metadata.py +++ b/src/saml2/metadata.py @@ -23,6 +23,7 @@ import xmldsig as ds from saml2.sigver import pre_signature_part from saml2.s_utils import factory +from saml2.s_utils import rec_factory from saml2.s_utils import sid __author__ = 'rolandh' @@ -51,6 +52,7 @@ ORG_ATTR_TRANSL = { "organization_url": ("url", md.OrganizationURL) } + def metadata_tostring_fix(desc, nspair): MDNS = '"urn:oasis:names:tc:SAML:2.0:metadata"' XMLNSXS = " xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" @@ -60,7 +62,7 @@ def metadata_tostring_fix(desc, nspair): return xmlstring -def create_metadata_string(configfile, config, valid, cert, keyfile, id, name, +def create_metadata_string(configfile, config, valid, cert, keyfile, mid, name, sign): valid_for = 0 nspair = {"xs": "http://www.w3.org/2001/XMLSchema"} @@ -85,8 +87,8 @@ def create_metadata_string(configfile, config, valid, cert, keyfile, id, name, conf.xmlsec_binary = config.xmlsec_binary secc = security_context(conf) - if id: - desc = entities_descriptor(eds, valid_for, name, id, + if mid: + desc = entities_descriptor(eds, valid_for, name, mid, sign, secc) valid_instance(desc) @@ -94,7 +96,7 @@ def create_metadata_string(configfile, config, valid, cert, keyfile, id, name, else: for eid in eds: if sign: - desc = sign_entity_descriptor(eid, id, secc) + desc = sign_entity_descriptor(eid, mid, secc) else: desc = eid valid_instance(desc) @@ -111,8 +113,9 @@ def _localized_name(val, klass): def do_organization_info(ava): - """ decription of an organization in the configuration is - a dictionary of keys and values, where the values might be tuples: + """ + Description of an organization in the configuration is + a dictionary of keys and values, where the values might be tuples:: "organization": { "name": ("AB Exempel", "se"), @@ -372,6 +375,21 @@ DEFAULT_BINDING = { } +def do_extensions(mname, item): + try: + _mod = __import__("saml2.extension.%s" % mname, globals(), locals(), + mname) + except ImportError: + return None + else: + res = [] + + for _cname, ava in item.items(): + cls = getattr(_mod, _cname) + res.append(rec_factory(cls, **ava)) + return res + + def _do_nameid_format(cls, conf, typ): namef = conf.getattr("name_id_format", typ) if namef: @@ -421,19 +439,30 @@ def do_spsso_descriptor(conf, cert=None): spsso = md.SPSSODescriptor() spsso.protocol_support_enumeration = samlp.NAMESPACE + exts = conf.getattr("extensions", "sp") + if exts: + if spsso.extensions is None: + spsso.extensions = md.Extensions() + + for key, val in exts.items(): + _ext = do_extensions(key, val) + if _ext: + for _e in _ext: + spsso.extensions.add_extension_element(_e) + endps = conf.getattr("endpoints", "sp") if endps: for (endpoint, instlist) in do_endpoints(endps, ENDPOINTS["sp"]).items(): setattr(spsso, endpoint, instlist) - ext = do_endpoints(endps, ENDPOINT_EXT["sp"]) - if ext: - if spsso.extensions is None: - spsso.extensions = md.Extensions() - for vals in ext.values(): - for val in vals: - spsso.extensions.add_extension_element(val) + # ext = do_endpoints(endps, ENDPOINT_EXT["sp"]) + # if ext: + # if spsso.extensions is None: + # spsso.extensions = md.Extensions() + # for vals in ext.values(): + # for val in vals: + # spsso.extensions.add_extension_element(val) if cert: encryption_type = conf.encryption_type @@ -445,7 +474,7 @@ def do_spsso_descriptor(conf, cert=None): if val is None: setattr(spsso, key, DEFAULT[key]) # default ?! else: - strval = "{0:>s}".format(val) + strval = "{0:>s}".format(str(val)) setattr(spsso, key, strval.lower()) except KeyError: setattr(spsso, key, DEFAULTS[key]) diff --git a/src/saml2/s_utils.py b/src/saml2/s_utils.py index 0343467..57fe088 100644 --- a/src/saml2/s_utils.py +++ b/src/saml2/s_utils.py @@ -413,7 +413,7 @@ def fticks_log(sp, logf, idp_entity_id, user_id, secret, assertion): "PN": csum.hexdigest(), "AM": ac.AuthnContextClassRef.text } - logf.info(FTICKS_FORMAT % "#".join(["%s=%s" % (a,v) for a,v in info])) + logf.info(FTICKS_FORMAT % "#".join(["%s=%s" % (a, v) for a, v in info])) def dynamic_importer(name, class_name=None): @@ -428,14 +428,14 @@ def dynamic_importer(name, class_name=None): try: package = imp.load_module(name, fp, pathname, description) - except Exception, e: + except Exception: raise if class_name: try: _class = imp.load_module("%s.%s" % (name, class_name), fp, - pathname, description) - except Exception, e: + pathname, description) + except Exception: raise return package, _class @@ -452,3 +452,34 @@ def exception_trace(exc): _exc = "Exception: %s" % exc.message.encode("utf-8", "replace") return {"message": _exc, "content": "".join(message)} + + +def rec_factory(cls, **kwargs): + _inst = cls() + for key, val in kwargs.items(): + if key in ["text", "lang"]: + setattr(_inst, key, val) + elif key in _inst.c_attributes: + try: + val = str(val) + except Exception: + continue + else: + setattr(_inst, key, val) + elif key in _inst.c_child_order: + for tag, _cls in _inst.c_children.values(): + if tag == key: + if isinstance(_cls, list): + _cls = _cls[0] + claim = [] + if isinstance(val, list): + for v in val: + claim.append(rec_factory(_cls, **v)) + else: + claim.append(rec_factory(_cls, **val)) + else: + claim = rec_factory(_cls, **val) + setattr(_inst, key, claim) + break + + return _inst diff --git a/src/saml2/validate.py b/src/saml2/validate.py index 3ba2dff..376df9e 100644 --- a/src/saml2/validate.py +++ b/src/saml2/validate.py @@ -201,6 +201,18 @@ def valid_unsigned_short(val): return True +def valid_positive_integer(val): + try: + integer = int(val) + except ValueError: + raise NotValid("positive integer") + + if integer > 0: + return True + else: + raise NotValid("positive integer") + + def valid_non_negative_integer(val): try: integer = int(val) @@ -269,6 +281,7 @@ VALIDATOR = { "dateTime": valid_date_time, "anyURI": valid_any_uri, "nonNegativeInteger": valid_non_negative_integer, + "PositiveInteger": valid_positive_integer, "boolean": valid_boolean, "unsignedShort": valid_unsigned_short, "duration": valid_duration, diff --git a/tests/enc_tmpl.xml b/tests/enc_tmpl.xml new file mode 100644 index 0000000..599a943 --- /dev/null +++ b/tests/enc_tmpl.xml @@ -0,0 +1,22 @@ + + + + + + + + my-rsa-key + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_19_attribute_converter.py b/tests/test_19_attribute_converter.py index 70d7b9d..177d509 100644 --- a/tests/test_19_attribute_converter.py +++ b/tests/test_19_attribute_converter.py @@ -7,6 +7,7 @@ from attribute_statement_data import * from pathutils import full_path from saml2.attribute_converter import AttributeConverterNOOP from saml2.attribute_converter import to_local +from saml2.saml import attribute_from_string def _eq(l1,l2): @@ -205,8 +206,23 @@ def test_noop_attribute_conversion(): assert attr.attribute_value[0].text == "Roland" +ava = """ +uu.se""" + + +def test_schac(): + attr = attribute_from_string(ava) + acs = attribute_converter.ac_factory() + for ac in acs: + try: + res = ac.ava_from(attr) + assert res[0] == "schacHomeOrganization" + except KeyError: + pass + + if __name__ == "__main__": - t = TestAC() - t.setup_class() - t.test_mixed_attributes_1() - #test_noop_attribute_conversion() + # t = TestAC() + # t.setup_class() + # t.test_mixed_attributes_1() + test_schac() diff --git a/tests/test_42_enc.py b/tests/test_42_enc.py new file mode 100644 index 0000000..1a73b83 --- /dev/null +++ b/tests/test_42_enc.py @@ -0,0 +1,99 @@ +from saml2.authn_context import INTERNETPROTOCOLPASSWORD +from saml2.server import Server +from saml2.sigver import pre_encryption_part, ASSERT_XPATH, EncryptError +from saml2.sigver import CryptoBackendXmlSec1 +from saml2.sigver import pre_encrypt_assertion +from pathutils import xmlsec_path + +__author__ = 'roland' + +TMPL = """ +my-rsa-key""" + +IDENTITY = {"eduPersonAffiliation": ["staff", "member"], + "surName": ["Jeter"], "givenName": ["Derek"], + "mail": ["foo@gmail.com"], + "title": ["shortstop"]} + + +AUTHN = { + "class_ref": INTERNETPROTOCOLPASSWORD, + "authn_auth": "http://www.example.com/login" +} + + +def test_pre_enc(): + tmpl = pre_encryption_part() + print tmpl + assert "%s" % tmpl == TMPL + + +def test_reshuffle_response(): + server = Server("idp_conf") + name_id = server.ident.transient_nameid( + "urn:mace:example.com:saml:roland:sp", "id12") + + resp_ = server.create_authn_response( + IDENTITY, "id12", "http://lingon.catalogix.se:8087/", + "urn:mace:example.com:saml:roland:sp", name_id=name_id) + + resp2 = pre_encrypt_assertion(resp_) + + print resp2 + assert resp2.encrypted_assertion.extension_elements + + +def test_enc1(): + server = Server("idp_conf") + name_id = server.ident.transient_nameid( + "urn:mace:example.com:saml:roland:sp", "id12") + + resp_ = server.create_authn_response( + IDENTITY, "id12", "http://lingon.catalogix.se:8087/", + "urn:mace:example.com:saml:roland:sp", name_id=name_id) + + statement = pre_encrypt_assertion(resp_) + + tmpl = "enc_tmpl.xml" + # tmpl_file = open(tmpl, "w") + # tmpl_file.write("%s" % pre_encryption_part()) + # tmpl_file.close() + + data = "pre_enc.xml" + # data_file = open(data, "w") + # data_file.write("%s" % statement) + # data_file.close() + + key_type = "des-192" + com_list = [xmlsec_path, "encrypt", "--pubkey-cert-pem", "pubkey.pem", + "--session-key", key_type, "--xml-data", data, + "--node-xpath", ASSERT_XPATH] + + crypto = CryptoBackendXmlSec1(xmlsec_path) + (_stdout, _stderr, output) = crypto._run_xmlsec( + com_list, [tmpl], exception=EncryptError, validate_output=False) + + print output + assert _stderr == "" + assert _stdout == "" + + +def test_enc2(): + crypto = CryptoBackendXmlSec1(xmlsec_path) + + server = Server("idp_conf") + name_id = server.ident.transient_nameid( + "urn:mace:example.com:saml:roland:sp", "id12") + + resp_ = server.create_authn_response( + IDENTITY, "id12", "http://lingon.catalogix.se:8087/", + "urn:mace:example.com:saml:roland:sp", name_id=name_id) + + enc_resp = crypto.encrypt_assertion(resp_, "pubkey.pem", + pre_encryption_part()) + + print enc_resp + assert enc_resp + +if __name__ == "__main__": + test_enc1() \ No newline at end of file diff --git a/tests/test_44_authnresp.py b/tests/test_44_authnresp.py index c6c405a..d4edb27 100644 --- a/tests/test_44_authnresp.py +++ b/tests/test_44_authnresp.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- from saml2.authn_context import INTERNETPROTOCOLPASSWORD -from saml2 import saml from saml2.server import Server from saml2.response import authn_response from saml2.config import config_factory diff --git a/tests/test_80_p11_backend.py b/tests/test_80_p11_backend.py index 069edf7..2ff49ec 100644 --- a/tests/test_80_p11_backend.py +++ b/tests/test_80_p11_backend.py @@ -4,7 +4,8 @@ Testing CryptoBackendXMLSecurity with SoftHSM as backend # Command to convert test.pem to PKCS#8 PEM format readable by softhsm : # -# openssl pkcs8 -topk8 -inform PEM -outform PEM -in test.key -out test.key.p8 -nocrypt +# openssl pkcs8 -topk8 -inform PEM -outform PEM -in test.key -out test.key.p8 +# -nocrypt # __author__ = 'leifj' # based on p11_test from pyXMLSecurity diff --git a/tests/test_83_md_extensions.py b/tests/test_83_md_extensions.py new file mode 100644 index 0000000..1f37f4b --- /dev/null +++ b/tests/test_83_md_extensions.py @@ -0,0 +1,14 @@ +from saml2.config import Config +from saml2.metadata import entity_descriptor + +__author__ = 'roland' + +fil = "sp_mdext_conf.py" + +cnf = Config().load_file(fil, metadata_construction=True) +ed = entity_descriptor(cnf) + +print ed + +assert ed.spsso_descriptor.extensions +assert len(ed.spsso_descriptor.extensions.extension_elements) == 3