diff options
author | rhoerbe <rainer@hoerbe.at> | 2014-09-22 09:47:58 +0200 |
---|---|---|
committer | rhoerbe <rainer@hoerbe.at> | 2014-09-22 09:47:58 +0200 |
commit | 816dcc76107cc23fb115ff449ac4afea45bb3a0d (patch) | |
tree | a18ba8124e96747660b2078d4d893f24731abec8 | |
parent | 2f4f2d6324a540123a114cadf7408acc7f67e684 (diff) | |
parent | 33db77a9ed5c00526e74dd5f9b4facf5be4a491e (diff) | |
download | pysaml2-816dcc76107cc23fb115ff449ac4afea45bb3a0d.tar.gz |
Merge https://github.com/rohe/pysaml2
Conflicts:
src/saml2/attributemaps/saml_uri.py
46 files changed, 734 insertions, 738 deletions
diff --git a/LICENSE.txt b/LICENSE.txt index 4192d2ce..70a5155d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2013 Roland Hedberg. All rights reserved. +Copyright 2014 Roland Hedberg. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/doc/conf.py b/doc/conf.py index a1340d26..1f7f2b52 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -39,7 +39,7 @@ master_doc = 'index' # General information about the project. project = u'pysaml2' -copyright = u'2010-2011, Roland Hedberg' +copyright = u'2014, Roland Hedberg' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/example/idp2/idp.py b/example/idp2/idp.py index edae5333..922ba139 100755 --- a/example/idp2/idp.py +++ b/example/idp2/idp.py @@ -51,6 +51,7 @@ from mako.lookup import TemplateLookup logger = logging.getLogger("saml2.idp") logger.setLevel(logging.WARNING) + class Cache(object): def __init__(self): self.user2uid = {} @@ -879,6 +880,7 @@ def metadata(environ, start_response): logger.error("An error occured while creating metadata:" + ex.message) return not_found(environ, start_response) + def staticfile(environ, start_response): try: path = args.path @@ -893,6 +895,7 @@ def staticfile(environ, start_response): logger.error("An error occured while creating metadata:" + ex.message) return not_found(environ, start_response) + def application(environ, start_response): """ The main WSGI application. Dispatch the current request to diff --git a/example/idp2/templates/root.mako b/example/idp2/templates/root.mako index c27e954a..f6b70cee 100644 --- a/example/idp2/templates/root.mako +++ b/example/idp2/templates/root.mako @@ -16,7 +16,7 @@ <%def name="post()" filter="trim"> <div> <div class="footer"> - <p>© Copyright 2011 Umeå Universitet </p> + <p>© Copyright 2014 Umeå Universitet </p> </div> </div> </%def> diff --git a/example/idp2_repoze/templates/root.mako b/example/idp2_repoze/templates/root.mako index c27e954a..f6b70cee 100644 --- a/example/idp2_repoze/templates/root.mako +++ b/example/idp2_repoze/templates/root.mako @@ -16,7 +16,7 @@ <%def name="post()" filter="trim"> <div> <div class="footer"> - <p>© Copyright 2011 Umeå Universitet </p> + <p>© Copyright 2014 Umeå Universitet </p> </div> </div> </%def> diff --git a/example/sp-wsgi/sp.xml b/example/sp-wsgi/sp.xml index db3c6df9..6c28d9c0 100644 --- a/example/sp-wsgi/sp.xml +++ b/example/sp-wsgi/sp.xml @@ -1,93 +1,34 @@ <?xml version='1.0' encoding='UTF-8'?> -<ns0:EntityDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" - xmlns:ns1="urn:oasis:names:tc:SAML:metadata:attribute" - xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion" - xmlns:ns4="http://www.w3.org/2000/09/xmldsig#" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - entityID="http://localhost:8087/sp.xml"> - <ns0:Extensions> - <ns1:EntityAttributes> - <ns2:Attribute Name="http://macedir.org/entity-category"> - <ns2:AttributeValue xsi:type="xs:string"> - http://www.geant.net/uri/dataprotection-code-of-conduct/v1 - </ns2:AttributeValue> - </ns2:Attribute> - </ns1:EntityAttributes> - </ns0:Extensions> - <ns0:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" - protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> - <ns0:KeyDescriptor use="encryption"> - <ns4:KeyInfo> - <ns4:X509Data> - <ns4:X509Certificate> - MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV - BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx - EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz - MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l - YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw - DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 - bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC - FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR - mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW - BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 - o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW - BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE - AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF - BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO - zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN - +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= - </ns4:X509Certificate> - </ns4:X509Data> - </ns4:KeyInfo> - </ns0:KeyDescriptor> - <ns0:KeyDescriptor use="signing"> - <ns4:KeyInfo> - <ns4:X509Data> - <ns4:X509Certificate> - MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV - BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx - EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz - MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l - YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw - DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 - bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC - FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR - mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW - BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 - o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW - BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE - AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF - BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO - zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN - +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= - </ns4:X509Certificate> - </ns4:X509Data> - </ns4:KeyInfo> - </ns0:KeyDescriptor> - <ns0:AssertionConsumerService - Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - Location="http://localhost:8087/acs/redirect" index="1"/> - <ns0:AssertionConsumerService - Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - Location="http://localhost:8087/acs/post" index="2"/> - <ns0:AttributeConsumingService index="1"> - <ns0:ServiceName xml:lang="en">My SP service</ns0:ServiceName> - <ns0:ServiceDescription xml:lang="en">Example SP - </ns0:ServiceDescription> - <ns0:RequestedAttribute FriendlyName="sn" Name="urn:oid:2.5.4.4" - NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" - isRequired="true"/> - <ns0:RequestedAttribute FriendlyName="givenname" - Name="urn:oid:2.5.4.42" - NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" - isRequired="true"/> - <ns0:RequestedAttribute FriendlyName="edupersonaffiliation" - Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" - NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" - isRequired="true"/> - <ns0:RequestedAttribute FriendlyName="title" Name="urn:oid:2.5.4.12" - NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" - isRequired="false"/> - </ns0:AttributeConsumingService> - </ns0:SPSSODescriptor> -</ns0:EntityDescriptor> +<ns0:EntityDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns1="urn:oasis:names:tc:SAML:metadata:attribute" xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns4="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" entityID="http://localhost:8087/sp.xml"><ns0:Extensions><ns1:EntityAttributes><ns2:Attribute Name="http://macedir.org/entity-category"><ns2:AttributeValue xsi:type="xs:string">http://www.geant.net/uri/dataprotection-code-of-conduct/v1</ns2:AttributeValue></ns2:Attribute></ns1:EntityAttributes></ns0:Extensions><ns0:SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:KeyDescriptor use="encryption"><ns4:KeyInfo><ns4:X509Data><ns4:X509Certificate>MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV +BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx +EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz +MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l +YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw +DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 +bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC +FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR +mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW +BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 +o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW +BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE +AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF +BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO +zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN ++vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= +</ns4:X509Certificate></ns4:X509Data></ns4:KeyInfo></ns0:KeyDescriptor><ns0:KeyDescriptor use="signing"><ns4:KeyInfo><ns4:X509Data><ns4:X509Certificate>MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV +BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx +EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz +MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l +YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw +DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 +bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC +FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR +mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW +BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 +o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW +BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE +AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF +BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO +zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN ++vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= +</ns4:X509Certificate></ns4:X509Data></ns4:KeyInfo></ns0:KeyDescriptor><ns0:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8087/acs/redirect" index="1" /><ns0:AttributeConsumingService index="1"><ns0:ServiceName xml:lang="en">My SP service</ns0:ServiceName><ns0:ServiceDescription xml:lang="en">Example SP</ns0:ServiceDescription><ns0:RequestedAttribute FriendlyName="sn" Name="urn:oid:2.5.4.4" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="givenname" Name="urn:oid:2.5.4.42" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="edupersonaffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.1" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true" /><ns0:RequestedAttribute FriendlyName="title" Name="urn:oid:2.5.4.12" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false" /></ns0:AttributeConsumingService></ns0:SPSSODescriptor></ns0:EntityDescriptor> @@ -1,27 +1,10 @@ #!/usr/bin/env python -# -# Copyright (C) 2007 SIOS Technology, Inc. -# Copyright (C) 2011 Umea Universitet, Sweden -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# + import sys from setuptools import setup from setuptools.command.test import test as TestCommand - class PyTest(TestCommand): def finalize_options(self): @@ -84,7 +67,9 @@ setup( package_data={'': ['xml/*.xml']}, classifiers=["Development Status :: 4 - Beta", "License :: OSI Approved :: Apache Software License", - "Topic :: Software Development :: Libraries :: Python Modules"], + "Topic :: Software Development :: Libraries :: Python Modules", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7"], scripts=["tools/parse_xsd2.py", "tools/make_metadata.py", "tools/mdexport.py", "tools/merge_metadata.py"], diff --git a/src/s2repoze/__init__.py b/src/s2repoze/__init__.py index 94e6145b..e3620e55 100644 --- a/src/s2repoze/__init__.py +++ b/src/s2repoze/__init__.py @@ -1,3 +1,2 @@ # -*- coding: utf-8 -*- # Created by Roland Hedberg -# Copyright (c) 2009 Umeå Universitet. All rights reserved. diff --git a/src/s2repoze/plugins/__init__.py b/src/s2repoze/plugins/__init__.py index a53880b5..40a96afc 100644 --- a/src/s2repoze/plugins/__init__.py +++ b/src/s2repoze/plugins/__init__.py @@ -1,3 +1 @@ # -*- coding: utf-8 -*- -# Created by Roland Hedberg -# Copyright (c) 2009 Umeå Universitet. All rights reserved. diff --git a/src/s2repoze/plugins/sp.py b/src/s2repoze/plugins/sp.py index 5bd7897c..60a34e54 100644 --- a/src/s2repoze/plugins/sp.py +++ b/src/s2repoze/plugins/sp.py @@ -1,17 +1,4 @@ -# Copyright (C) 2009 Umea University # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - """ A plugin that allows you to use SAML2 SSO as authentication and SAML2 attribute aggregations as metadata collector in your diff --git a/src/saml2/assertion.py b/src/saml2/assertion.py index 7a361aed..e7410672 100644 --- a/src/saml2/assertion.py +++ b/src/saml2/assertion.py @@ -1,19 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# -# Copyright (C) 2010-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. import importlib import logging diff --git a/src/saml2/attribute_converter.py b/src/saml2/attribute_converter.py index 49d00bf0..bc39ca54 100644 --- a/src/saml2/attribute_converter.py +++ b/src/saml2/attribute_converter.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) s2010-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. import os import sys diff --git a/src/saml2/attribute_resolver.py b/src/saml2/attribute_resolver.py index dab809ce..e02fd8a6 100644 --- a/src/saml2/attribute_resolver.py +++ b/src/saml2/attribute_resolver.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """ Contains classes and functions that a SAML2.0 Service Provider (SP) may use diff --git a/src/saml2/attributemaps/saml_uri.py b/src/saml2/attributemaps/saml_uri.py index 840b6619..fd5d51c7 100644 --- a/src/saml2/attributemaps/saml_uri.py +++ b/src/saml2/attributemaps/saml_uri.py @@ -1,16 +1,16 @@ EDUCOURSE_OID = 'urn:oid:1.3.6.1.4.1.5923.1.6.1.' EDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.5923.1.1.1.' +LDAPGVAT_OID = 'urn:oid:1.2.40.0.10.2.1.1.' # ldap.gv.at definitions as specified in http://www.ref.gv.at/AG-IZ-PVP2-Version-2-1-0-2.2754.0.html +UCL_DIR_PILOT = 'urn:oid:0.9.2342.19200300.100.1.' +X500ATTR_OID = 'urn:oid:2.5.4.' +LDAPGVAT_UCL_DIR_PILOT = UCL_DIR_PILOT +LDAPGVAT_X500ATTR_OID = X500ATTR_OID NETSCAPE_LDAP = 'urn:oid:2.16.840.1.113730.3.1.' NOREDUPERSON_OID = 'urn:oid:1.3.6.1.4.1.2428.90.1.' PKCS_9 = 'urn:oid:1.2.840.113549.1.9.1.' SCHAC = 'urn:oid:1.3.6.1.4.1.25178.1.2.' SIS = 'urn:oid:1.2.752.194.10.2.' -UCL_DIR_PILOT = 'urn:oid:0.9.2342.19200300.100.1.' UMICH = 'urn:oid:1.3.6.1.4.1.250.1.57.' -X500ATTR_OID = 'urn:oid:2.5.4.' -LDAPGVAT_OID = 'urn:oid:1.2.40.0.10.2.1.1.' # ldap.gv.at definitions as specified in http://www.ref.gv.at/AG-IZ-PVP2-Version-2-1-0-2.2754.0.html -LDAPGVAT_UCL_DIR_PILOT = UCL_DIR_PILOT -LDAPGVAT_X500ATTR_OID = X500ATTR_OID MAP = { diff --git a/src/saml2/client.py b/src/saml2/client.py index 3a7848ba..27afc2af 100644 --- a/src/saml2/client.py +++ b/src/saml2/client.py @@ -1,19 +1,6 @@ -#!/usr/bin/env python +# !/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Contains classes and functions that a SAML2.0 Service Provider (SP) may use to conclude its tasks. @@ -26,7 +13,7 @@ from saml2 import BINDING_HTTP_REDIRECT from saml2 import BINDING_HTTP_POST from saml2 import BINDING_SOAP -from saml2.ident import decode +from saml2.ident import decode, code from saml2.httpbase import HTTPError from saml2.s_utils import sid from saml2.s_utils import status_message_factory @@ -48,6 +35,7 @@ except ImportError: from cgi import parse_qs import logging + logger = logging.getLogger(__name__) @@ -80,9 +68,10 @@ class Saml2Client(Base): destination = self._sso_location(entityid, binding) reqid, req = self.create_authn_request(destination, vorg, scoping, - response_binding, nameid_format, - consent=consent, extensions=extensions, - sign=sign, **kwargs) + response_binding, nameid_format, + consent=consent, + extensions=extensions, sign=sign, + **kwargs) _req_str = "%s" % req logger.info("AuthNReq: %s" % _req_str) @@ -117,7 +106,7 @@ class Saml2Client(Base): # find out which IdPs/AAs I should notify entity_ids = self.users.issuers_of_info(name_id) return self.do_logout(name_id, entity_ids, reason, expire, sign) - + def do_logout(self, name_id, entity_ids, reason, expire, sign=None, expected_binding=None): """ @@ -137,7 +126,7 @@ class Saml2Client(Base): # Do the local logout anyway self.local_logout(name_id) return 0, "504 Gateway Timeout", [], [] - + not_done = entity_ids[:] responses = {} @@ -164,7 +153,7 @@ class Saml2Client(Base): req_id, request = self.create_logout_request( destination, entity_id, name_id=name_id, reason=reason, expire=expire) - + #to_sign = [] if binding.startswith("http://"): sign = True @@ -196,12 +185,12 @@ class Saml2Client(Base): else: self.state[req_id] = {"entity_id": entity_id, - "operation": "SLO", - "entity_ids": entity_ids, - "name_id": name_id, - "reason": reason, - "not_on_of_after": expire, - "sign": sign} + "operation": "SLO", + "entity_ids": entity_ids, + "name_id": code(name_id), + "reason": reason, + "not_on_of_after": expire, + "sign": sign} responses[entity_id] = (binding, http_info) not_done.remove(entity_id) @@ -212,7 +201,7 @@ class Saml2Client(Base): if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids,)) - + return responses def local_logout(self, name_id): @@ -230,7 +219,7 @@ class Saml2Client(Base): """ identity = self.users.get_identity(name_id)[0] return bool(identity) - + def handle_logout_response(self, response): """ handles a Logout response @@ -246,11 +235,12 @@ class Saml2Client(Base): logger.info("issuer: %s" % issuer) del self.state[response.in_response_to] if status["entity_ids"] == [issuer]: # done - self.local_logout(status["name_id"]) + self.local_logout(decode(status["name_id"])) return 0, "200 Ok", [("Content-type", "text/html")], [] else: status["entity_ids"].remove(issuer) - return self.do_logout(status["name_id"], status["entity_ids"], + return self.do_logout(decode(status["name_id"]), + status["entity_ids"], status["reason"], status["not_on_or_after"], status["sign"]) diff --git a/src/saml2/client_base.py b/src/saml2/client_base.py index f4676d23..d5283d6f 100644 --- a/src/saml2/client_base.py +++ b/src/saml2/client_base.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Contains classes and functions that a SAML2.0 Service Provider (SP) may use to conclude its tasks. @@ -191,7 +178,8 @@ class Base(Entity): return ava #noinspection PyUnusedLocal - def is_session_valid(self, _session_id): + @staticmethod + def is_session_valid(_session_id): """ Place holder. Supposed to check if the session is still valid. """ return True @@ -399,7 +387,7 @@ class Base(Entity): return self._message(AuthzDecisionQuery, destination, message_id, consent, extensions, sign, action=action, evidence=evidence, resource=resource, - subject=subject) + subject=subject, **kwargs) def create_authz_decision_query_using_assertion(self, destination, assertion, action=None, @@ -436,7 +424,8 @@ class Base(Entity): resource, subject, message_id=message_id, consent=consent, extensions=extensions, sign=sign) - def create_assertion_id_request(self, assertion_id_refs, **kwargs): + @staticmethod + def create_assertion_id_request(assertion_id_refs, **kwargs): """ :param assertion_id_refs: @@ -534,7 +523,7 @@ class Base(Entity): "entity_id": self.config.entityid, "attribute_converters": self.config.attribute_converters, "allow_unknown_attributes": - self.config.allow_unknown_attributes, + self.config.allow_unknown_attributes, } try: resp = self._parse_response(xmlstr, AuthnResponse, diff --git a/src/saml2/config.py b/src/saml2/config.py index 11ef7857..e704fa39 100644 --- a/src/saml2/config.py +++ b/src/saml2/config.py @@ -71,7 +71,8 @@ COMMON_ARGS = [ "tmp_cert_file", "tmp_key_file", "validate_certificate", - "extensions" + "extensions", + "allow_unknown_attributes" ] SP_ARGS = [ @@ -90,7 +91,6 @@ SP_ARGS = [ "allow_unsolicited", "ecp", "name_id_format", - "allow_unknown_attributes" ] AA_IDP_ARGS = [ @@ -216,6 +216,7 @@ class Config(object): self.crypto_backend = 'xmlsec1' self.scope = "" self.allow_unknown_attributes = False + self.allow_unsolicited = False self.extension_schema = {} self.cert_handler_extra_class = None self.verify_encrypt_cert = None @@ -400,9 +401,9 @@ class Config(object): def endpoint(self, service, binding=None, context=None): """ Goes through the list of endpoint specifications for the - given type of service and returnes the first endpoint that matches - the given binding. If no binding is given any endpoint for that - service will be returned. + given type of service and returns a list of endpoint that matches + the given binding. If no binding is given all endpoints available for + that service will be returned. :param service: The service the endpoint should support :param binding: The expected binding diff --git a/src/saml2/ecp.py b/src/saml2/ecp.py index 4a1a0c19..e99e174a 100644 --- a/src/saml2/ecp.py +++ b/src/saml2/ecp.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2010-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """ Contains classes used in the SAML ECP profile diff --git a/src/saml2/ecp_client.py b/src/saml2/ecp_client.py index beb638a5..b9f4573c 100644 --- a/src/saml2/ecp_client.py +++ b/src/saml2/ecp_client.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2010-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """ Contains a class that can do SAML ECP Authentication for other python diff --git a/src/saml2/httpbase.py b/src/saml2/httpbase.py index 56d4a7e5..eeb0a566 100644 --- a/src/saml2/httpbase.py +++ b/src/saml2/httpbase.py @@ -177,7 +177,7 @@ class HTTPBase(object): std_attr[attr] = morsel[attr] elif attr == "max-age": if morsel["max-age"]: - std_attr["expires"] = _since_epoch(morsel["max-age"]) + std_attr["expires"] = time.time() + int(morsel["max-age"]) for att, item in PAIRS.items(): if std_attr[att]: diff --git a/src/saml2/ident.py b/src/saml2/ident.py index 0e51cc83..162f3fac 100644 --- a/src/saml2/ident.py +++ b/src/saml2/ident.py @@ -26,6 +26,15 @@ class Unknown(SAMLError): def code(item): + """ + Turn a NameID class instance into a quoted string of comma separated + attribute,value pairs. The attribute name is replaced with a digits. + Depends on knowledge on the specific order of the attributes for that + class that is used. + + :param item: The class instance + :return: A quoted string + """ _res = [] i = 0 for attr in ATTR: @@ -37,6 +46,10 @@ def code(item): def decode(txt): + """Turns a coded string by code() into a NameID class instance. + + :param txt: The coded string + """ _nid = NameID() for part in txt.split(","): if part.find("=") != -1: diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py index fe450e7d..f7ce467e 100644 --- a/src/saml2/mdstore.py +++ b/src/saml2/mdstore.py @@ -16,7 +16,7 @@ from saml2 import SAMLError from saml2 import BINDING_HTTP_REDIRECT from saml2 import BINDING_HTTP_POST from saml2 import BINDING_SOAP -from saml2.s_utils import UnsupportedBinding, UnknownPrincipal +from saml2.s_utils import UnsupportedBinding, UnknownSystemEntity from saml2.sigver import split_len from saml2.validate import valid_instance from saml2.time_util import valid @@ -67,9 +67,12 @@ def destinations(srvs): return [s["location"] for s in srvs] -def attribute_requirement(entity): +def attribute_requirement(entity, index=None): res = {"required": [], "optional": []} for acs in entity["attribute_consuming_service"]: + if index is not None and acs["index"] != index: + continue + for attr in acs["requested_attribute"]: if "is_required" in attr and attr["is_required"] == "true": res["required"].append(attr) @@ -127,12 +130,21 @@ class MetaData(object): def values(self): return self.entity.values() + def __len__(self): + return len(self.entity) + def __contains__(self, item): - return item in self.entity + return item in self.entity.keys() def __getitem__(self, item): return self.entity[item] + def __setitem__(self, key, value): + self.entity[key] = value + + def __delitem__(self, key): + del self.entity[key] + def do_entity_descriptor(self, entity_descr): if self.check_validity: try: @@ -221,7 +233,7 @@ class MetaData(object): """ logger.debug("service(%s, %s, %s, %s)" % (entity_id, typ, service, - binding)) + binding)) try: srvs = [] for t in self[entity_id][typ]: @@ -297,12 +309,14 @@ class MetaData(object): return self.service(entity_id, typ, service) - def attribute_requirement(self, entity_id, index=0): + def attribute_requirement(self, entity_id, index=None): """ Returns what attributes the SP requires and which are optional if any such demands are registered in the Metadata. :param entity_id: The entity id of the SP :param index: which of the attribute consumer services its all about + if index=None then return all attributes expected by all + attribute_consuming_services. :return: 2-tuple, list of required and list of optional attributes """ @@ -310,7 +324,7 @@ class MetaData(object): try: for sp in self[entity_id]["spsso_descriptor"]: - _res = attribute_requirement(sp) + _res = attribute_requirement(sp, index) res["required"].extend(_res["required"]) res["optional"].extend(_res["optional"]) except KeyError: @@ -513,6 +527,7 @@ class MetaDataMD(MetaData): class MetadataStore(object): def __init__(self, onts, attrc, config, ca_certs=None, + check_validity=True, disable_ssl_certificate_validation=False): """ :params onts: @@ -523,11 +538,16 @@ class MetadataStore(object): """ self.onts = onts self.attrc = attrc - self.http = HTTPBase(verify=disable_ssl_certificate_validation, - ca_bundle=ca_certs) + + if disable_ssl_certificate_validation: + self.http = HTTPBase(verify=False, ca_bundle=ca_certs) + else: + self.http = HTTPBase(verify=True, ca_bundle=ca_certs) + self.security = security_context(config) self.ii = 0 self.metadata = {} + self.check_validity = check_validity def load(self, typ, *args, **kwargs): if typ == "local": @@ -539,10 +559,16 @@ class MetadataStore(object): _md = MetaData(self.onts, self.attrc, args[0], **kwargs) elif typ == "remote": key = kwargs["url"] + _args = {} + for _key in ["node_name", "check_validity"]: + try: + _args[_key] = kwargs[_key] + except KeyError: + pass + _md = MetaDataExtern(self.onts, self.attrc, kwargs["url"], self.security, - kwargs["cert"], self.http, - node_name=kwargs.get('node_name')) + kwargs["cert"], self.http, **_args) elif typ == "mdfile": key = args[0] _md = MetaDataMD(self.onts, self.attrc, args[0]) @@ -559,12 +585,14 @@ class MetadataStore(object): for key, vals in spec.items(): for val in vals: if isinstance(val, dict): + if not self.check_validity: + val["check_validity"] = False self.load(key, **val) else: self.load(key, val) def service(self, entity_id, typ, service, binding=None): - known_principal = False + known_entity = False for key, _md in self.metadata.items(): srvs = _md.service(entity_id, typ, service, binding) if srvs: @@ -572,17 +600,17 @@ class MetadataStore(object): elif srvs is None: pass else: - known_principal = True + known_entity = True - if known_principal: + if known_entity: logger.error("Unsupported binding: %s (%s)" % (binding, entity_id)) raise UnsupportedBinding(binding) else: - logger.error("Unknown principal: %s" % entity_id) - raise UnknownPrincipal(entity_id) + logger.error("Unknown system entity: %s" % entity_id) + raise UnknownSystemEntity(entity_id) def ext_service(self, entity_id, typ, service, binding=None): - known_principal = False + known_entity = False for key, _md in self.metadata.items(): srvs = _md.ext_service(entity_id, typ, service, binding) if srvs: @@ -590,12 +618,12 @@ class MetadataStore(object): elif srvs is None: pass else: - known_principal = True + known_entity = True - if known_principal: + if known_entity: raise UnsupportedBinding(binding) else: - raise UnknownPrincipal(entity_id) + raise UnknownSystemEntity(entity_id) def single_sign_on_service(self, entity_id, binding=None, typ="idpsso"): # IDP @@ -633,7 +661,7 @@ class MetadataStore(object): if binding is None: binding = BINDING_SOAP return self.service(entity_id, "pdp_descriptor", - "authz_service", binding) + "authz_service", binding) def assertion_id_request_service(self, entity_id, binding=None, typ=None): # AuthnAuthority + IDP + PDP + AttributeAuthority @@ -642,7 +670,7 @@ class MetadataStore(object): if binding is None: binding = BINDING_SOAP return self.service(entity_id, "%s_descriptor" % typ, - "assertion_id_request_service", binding) + "assertion_id_request_service", binding) def single_logout_service(self, entity_id, binding=None, typ=None): # IDP + SP @@ -651,35 +679,35 @@ class MetadataStore(object): if binding is None: binding = BINDING_HTTP_REDIRECT return self.service(entity_id, "%s_descriptor" % typ, - "single_logout_service", binding) + "single_logout_service", binding) def manage_name_id_service(self, entity_id, binding=None, typ=None): # IDP + SP if binding is None: binding = BINDING_HTTP_REDIRECT return self.service(entity_id, "%s_descriptor" % typ, - "manage_name_id_service", binding) + "manage_name_id_service", binding) def artifact_resolution_service(self, entity_id, binding=None, typ=None): # IDP + SP if binding is None: binding = BINDING_HTTP_REDIRECT return self.service(entity_id, "%s_descriptor" % typ, - "artifact_resolution_service", binding) + "artifact_resolution_service", binding) def assertion_consumer_service(self, entity_id, binding=None, _="spsso"): # SP if binding is None: binding = BINDING_HTTP_POST return self.service(entity_id, "spsso_descriptor", - "assertion_consumer_service", binding) + "assertion_consumer_service", binding) def attribute_consuming_service(self, entity_id, binding=None, _="spsso"): # SP if binding is None: binding = BINDING_HTTP_REDIRECT return self.service(entity_id, "spsso_descriptor", - "attribute_consuming_service", binding) + "attribute_consuming_service", binding) def discovery_response(self, entity_id, binding=None, _="spsso"): if binding is None: @@ -863,7 +891,11 @@ class MetadataStore(object): for _md in self.metadata.values(): for ent_id, ent_desc in _md.items(): if descriptor in ent_desc: - res.append(ent_id) + if ent_id in res: + #print "duplicated entity_id: %s" % res + pass + else: + res.append(ent_id) return res def service_providers(self): @@ -887,7 +919,8 @@ class MetadataStore(object): res = EntitiesDescriptor() for _md in self.metadata.values(): try: - res.entity_descriptor.extend(_md.entities_descr.entity_descriptor) + res.entity_descriptor.extend( + _md.entities_descr.entity_descriptor) except AttributeError: res.entity_descriptor.append(_md.entity_descr) diff --git a/src/saml2/metadata.py b/src/saml2/metadata.py index 3491bef5..8d81f371 100644 --- a/src/saml2/metadata.py +++ b/src/saml2/metadata.py @@ -423,9 +423,18 @@ def do_endpoints(conf, endpoints): args = {"location": args[0], "binding": args[1], "index": args[2]} - if indexed and "index" not in args: - args["index"] = "%d" % i - i += 1 + if indexed: + if "index" not in args: + args["index"] = "%d" % i + i += 1 + else: + try: + int(args["index"]) + except ValueError: + raise + else: + args["index"] = str(args["index"]) + servs.append(factory(eclass, **args)) service[endpoint] = servs except KeyError: diff --git a/src/saml2/pack.py b/src/saml2/pack.py index 53c31ccd..a7b86857 100644 --- a/src/saml2/pack.py +++ b/src/saml2/pack.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2010-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Contains classes and functions that are necessary to implement different bindings. diff --git a/src/saml2/request.py b/src/saml2/request.py index e8348084..f840db1d 100644 --- a/src/saml2/request.py +++ b/src/saml2/request.py @@ -75,7 +75,7 @@ class Request(object): def _verify(self): assert self.message.version == "2.0" - if self.message.destination and \ + if self.message.destination and self.receiver_addrs and \ self.message.destination not in self.receiver_addrs: logger.error("%s not in %s" % (self.message.destination, self.receiver_addrs)) diff --git a/src/saml2/response.py b/src/saml2/response.py index f6a8d187..b7c8fa99 100644 --- a/src/saml2/response.py +++ b/src/saml2/response.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2010-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. import calendar import logging diff --git a/src/saml2/s_utils.py b/src/saml2/s_utils.py index 57fe0886..0a2aa5c2 100644 --- a/src/saml2/s_utils.py +++ b/src/saml2/s_utils.py @@ -47,6 +47,10 @@ class UnknownPrincipal(SamlException): pass +class UnknownSystemEntity(SamlException): + pass + + class Unsupported(SamlException): pass diff --git a/src/saml2/server.py b/src/saml2/server.py index 954b1ec3..f7512add 100644 --- a/src/saml2/server.py +++ b/src/saml2/server.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Contains classes and functions that a SAML2.0 Identity provider (IdP) or attribute authority (AA) may use to conclude its tasks. @@ -187,6 +174,8 @@ class Server(Entity): :param sp_entity_id: The entity id of the SP :param index: which of the attribute consumer services its all about + if index == None then all attribute consumer services are clumped + together. :return: 2-tuple, list of required and list of optional attributes """ return self.metadata.attribute_requirement(sp_entity_id, index) diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index cbbbcbee..c32136ad 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """ Functions connected to signing and verifying. Based on the use of xmlsec1 binaries and not the python xmlsec module. @@ -123,6 +110,11 @@ class CertificateError(SigverError): pass +def read_file(*args, **kwargs): + with open(*args, **kwargs) as handler: + return handler.read() + + def rm_xmltag(statement): try: _t = statement.startswith(XMLTAG) @@ -557,7 +549,7 @@ def pem_format(key): def import_rsa_key_from_file(filename): - return RSA.importKey(open(filename, 'r').read()) + return RSA.importKey(read_file(filename, 'r')) def parse_xmlsec_output(output): @@ -644,14 +636,11 @@ def verify_redirect_signature(saml_msg, cert): args = saml_msg.copy() del args["Signature"] # everything but the signature string = "&".join( - [urllib.urlencode({k: args[k][0]}) for k in _order]) + [urllib.urlencode({k: args[k][0]}) for k in _order if k in args]) _key = extract_rsa_key_from_x509_cert(pem_format(cert)) _sign = base64.b64decode(saml_msg["Signature"][0]) - try: - signer.verify(string, _sign, _key) - return True - except BadSignature: - return False + + return bool(signer.verify(string, _sign, _key)) LOG_LINE = 60 * "=" + "\n%s\n" + 60 * "-" + "\n%s" + 60 * "=" @@ -669,11 +658,13 @@ def read_cert_from_file(cert_file, cert_type): :param cert_type: The certificate type :return: A base64 encoded certificate as a string or the empty string """ + + if not cert_file: return "" if cert_type == "pem": - line = open(cert_file).read().split("\n") + line = read_file(cert_file).split("\n") if line[0] == "-----BEGIN CERTIFICATE-----": line = line[1:] elif line[0] == "-----BEGIN PUBLIC KEY-----": @@ -693,7 +684,7 @@ def read_cert_from_file(cert_file, cert_type): return "".join(line) if cert_type in ["der", "cer", "crt"]: - data = open(cert_file).read() + data = read_file(cert_file) return base64.b64encode(str(data)) @@ -1013,9 +1004,15 @@ def security_context(conf, debug=None): return None if debug is None: - debug = conf.debug + try: + debug = conf.debug + except AttributeError: + pass - metadata = conf.metadata + try: + metadata = conf.metadata + except AttributeError: + metadata = None _only_md = conf.only_use_keys_in_metadata if _only_md is None: @@ -1063,13 +1060,19 @@ def encrypt_cert_from_item(item): certs = cert_from_instance(item) if len(certs) > 0: _encrypt_cert = certs[0] - if _encrypt_cert is not None: - if _encrypt_cert.find("-----BEGIN CERTIFICATE-----\n") == -1: - _encrypt_cert = "-----BEGIN CERTIFICATE-----\n" + _encrypt_cert - if _encrypt_cert.find("-----END CERTIFICATE-----\n") == -1: - _encrypt_cert = _encrypt_cert + "-----END CERTIFICATE-----\n" except Exception: - return None + pass + + if _encrypt_cert is None: + certs = cert_from_instance(item) + if len(certs) > 0: + _encrypt_cert = certs[0] + + if _encrypt_cert is not None: + if _encrypt_cert.find("-----BEGIN CERTIFICATE-----\n") == -1: + _encrypt_cert = "-----BEGIN CERTIFICATE-----\n" + _encrypt_cert + if _encrypt_cert.find("\n-----END CERTIFICATE-----") == -1: + _encrypt_cert = _encrypt_cert + "\n-----END CERTIFICATE-----" return _encrypt_cert @@ -1835,4 +1838,4 @@ if __name__ == '__main__': args = parser.parse_args() if args.listsigalgs: - print '\n'.join([key for key, value in SIGNER_ALGS.items()])
\ No newline at end of file + print '\n'.join([key for key, value in SIGNER_ALGS.items()]) diff --git a/src/saml2/soap.py b/src/saml2/soap.py index 75329895..e42e8f9e 100644 --- a/src/saml2/soap.py +++ b/src/saml2/soap.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """ Suppport for the client part of the SAML2.0 SOAP binding. diff --git a/src/saml2/time_util.py b/src/saml2/time_util.py index 34df69e7..40d7d062 100644 --- a/src/saml2/time_util.py +++ b/src/saml2/time_util.py @@ -1,20 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2009-2011 Umeå University -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" +""" Implements some usefull functions when dealing with validity of different types of information. """ diff --git a/tests/ds_data.py b/tests/ds_data.py index 71743852..243d955e 100644 --- a/tests/ds_data.py +++ b/tests/ds_data.py @@ -1,18 +1,5 @@ #!/usr/bin/env python # -# Copyright (C) 2007 SIOS Technology, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Test data for ds""" diff --git a/tests/fakeIDP.py b/tests/fakeIDP.py index 3fd25a73..1df910e2 100644 --- a/tests/fakeIDP.py +++ b/tests/fakeIDP.py @@ -47,6 +47,7 @@ class DummyResponse(object): self.status_code = code self.text = data self.headers = headers or [] + self.content = data class FakeIDP(Server): @@ -142,12 +143,9 @@ class FakeIDP(Server): #userid = "Pavill" name_id = aquery.subject.name_id - attr_resp = self.create_attribute_response(extra, aquery.id, - None, - sp_entity_id=aquery.issuer - .text, - name_id=name_id, - attributes=aquery.attribute) + attr_resp = self.create_attribute_response( + extra, aquery.id, None, sp_entity_id=aquery.issuer.text, + name_id=name_id, attributes=aquery.attribute) if binding == BINDING_SOAP: # SOAP packing diff --git a/tests/idp_example.xml b/tests/idp_example.xml new file mode 100644 index 00000000..6c7efca9 --- /dev/null +++ b/tests/idp_example.xml @@ -0,0 +1,230 @@ +<?xml version='1.0' encoding='UTF-8'?> +<ns0:EntityDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" + xmlns:ns1="http://www.w3.org/2000/09/xmldsig#" + entityID="http://localhost:8088/idp.xml" + validUntil="2014-04-12T06:06:13Z"> + <ns0:IDPSSODescriptor WantAuthnRequestsOnlyWithValidCert="false" + WantAuthnRequestsSigned="false" + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <ns0:KeyDescriptor use="encryption"> + <ns1:KeyInfo> + <ns1:X509Data> + <ns1:X509Certificate> + MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV + BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx + EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz + MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l + YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw + DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 + bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC + FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR + mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW + BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 + o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW + BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE + AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF + BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO + zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN + +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= + </ns1:X509Certificate> + </ns1:X509Data> + </ns1:KeyInfo> + </ns0:KeyDescriptor> + <ns0:KeyDescriptor use="signing"> + <ns1:KeyInfo> + <ns1:X509Data> + <ns1:X509Certificate> + MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV + BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx + EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz + MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l + YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw + DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 + bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC + FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR + mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW + BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 + o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW + BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE + AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF + BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO + zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN + +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= + </ns1:X509Certificate> + </ns1:X509Data> + </ns1:KeyInfo> + </ns0:KeyDescriptor> + <ns0:SingleLogoutService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" + Location="http://localhost:8088/slo/soap"/> + <ns0:SingleLogoutService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + Location="http://localhost:8088/slo/post"/> + <ns0:SingleLogoutService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + Location="http://localhost:8088/slo/redirect"/> + <ns0:ManageNameIDService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" + Location="http://localhost:8088/mni/soap"/> + <ns0:ManageNameIDService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + Location="http://localhost:8088/mni/post"/> + <ns0:ManageNameIDService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + Location="http://localhost:8088/mni/redirect"/> + <ns0:ManageNameIDService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" + Location="http://localhost:8088/mni/art"/> + <ns0:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient + </ns0:NameIDFormat> + <ns0:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + </ns0:NameIDFormat> + <ns0:SingleSignOnService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + Location="http://localhost:8088/sso/redirect"/> + <ns0:SingleSignOnService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + Location="http://localhost:8088/sso/post"/> + <ns0:SingleSignOnService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" + Location="http://localhost:8088/sso/art"/> + <ns0:SingleSignOnService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" + Location="http://localhost:8088/sso/ecp"/> + <ns0:NameIDMappingService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" + Location="http://localhost:8088/nim"/> + <ns0:AssertionIDRequestService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" + Location="http://localhost:8088/airs"/> + </ns0:IDPSSODescriptor> + <ns0:AuthnAuthorityDescriptor + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <ns0:KeyDescriptor use="encryption"> + <ns1:KeyInfo> + <ns1:X509Data> + <ns1:X509Certificate> + MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV + BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx + EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz + MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l + YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw + DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 + bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC + FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR + mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW + BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 + o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW + BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE + AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF + BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO + zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN + +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= + </ns1:X509Certificate> + </ns1:X509Data> + </ns1:KeyInfo> + </ns0:KeyDescriptor> + <ns0:KeyDescriptor use="signing"> + <ns1:KeyInfo> + <ns1:X509Data> + <ns1:X509Certificate> + MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV + BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx + EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz + MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l + YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw + DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 + bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC + FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR + mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW + BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 + o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW + BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE + AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF + BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO + zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN + +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= + </ns1:X509Certificate> + </ns1:X509Data> + </ns1:KeyInfo> + </ns0:KeyDescriptor> + <ns0:AuthnQueryService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" + Location="http://localhost:8088/aqs"/> + </ns0:AuthnAuthorityDescriptor> + <ns0:AttributeAuthorityDescriptor + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <ns0:KeyDescriptor use="encryption"> + <ns1:KeyInfo> + <ns1:X509Data> + <ns1:X509Certificate> + MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV + BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx + EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz + MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l + YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw + DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 + bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC + FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR + mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW + BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 + o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW + BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE + AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF + BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO + zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN + +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= + </ns1:X509Certificate> + </ns1:X509Data> + </ns1:KeyInfo> + </ns0:KeyDescriptor> + <ns0:KeyDescriptor use="signing"> + <ns1:KeyInfo> + <ns1:X509Data> + <ns1:X509Certificate> + MIIC8jCCAlugAwIBAgIJAJHg2V5J31I8MA0GCSqGSIb3DQEBBQUAMFoxCzAJBgNV + BAYTAlNFMQ0wCwYDVQQHEwRVbWVhMRgwFgYDVQQKEw9VbWVhIFVuaXZlcnNpdHkx + EDAOBgNVBAsTB0lUIFVuaXQxEDAOBgNVBAMTB1Rlc3QgU1AwHhcNMDkxMDI2MTMz + MTE1WhcNMTAxMDI2MTMzMTE1WjBaMQswCQYDVQQGEwJTRTENMAsGA1UEBxMEVW1l + YTEYMBYGA1UEChMPVW1lYSBVbml2ZXJzaXR5MRAwDgYDVQQLEwdJVCBVbml0MRAw + DgYDVQQDEwdUZXN0IFNQMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDkJWP7 + bwOxtH+E15VTaulNzVQ/0cSbM5G7abqeqSNSs0l0veHr6/ROgW96ZeQ57fzVy2MC + FiQRw2fzBs0n7leEmDJyVVtBTavYlhAVXDNa3stgvh43qCfLx+clUlOvtnsoMiiR + mo7qf0BoPKTj7c0uLKpDpEbAHQT4OF1HRYVxMwIDAQABo4G/MIG8MB0GA1UdDgQW + BBQ7RgbMJFDGRBu9o3tDQDuSoBy7JjCBjAYDVR0jBIGEMIGBgBQ7RgbMJFDGRBu9 + o3tDQDuSoBy7JqFepFwwWjELMAkGA1UEBhMCU0UxDTALBgNVBAcTBFVtZWExGDAW + BgNVBAoTD1VtZWEgVW5pdmVyc2l0eTEQMA4GA1UECxMHSVQgVW5pdDEQMA4GA1UE + AxMHVGVzdCBTUIIJAJHg2V5J31I8MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF + BQADgYEAMuRwwXRnsiyWzmRikpwinnhTmbooKm5TINPE7A7gSQ710RxioQePPhZO + zkM27NnHTrCe2rBVg0EGz7QTd1JIwLPvgoj4VTi/fSha/tXrYUaqc9AqU1kWI4WN + +vffBGQ09mo+6CffuFTZYeOhzP/2stAPwCTU4kxEoiy0KpZMANI= + </ns1:X509Certificate> + </ns1:X509Data> + </ns1:KeyInfo> + </ns0:KeyDescriptor> + <ns0:AttributeService + Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" + Location="http://localhost:8088/attr"/> + <ns0:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient + </ns0:NameIDFormat> + <ns0:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + </ns0:NameIDFormat> + </ns0:AttributeAuthorityDescriptor> + <ns0:Organization> + <ns0:OrganizationName xml:lang="en">Rolands Identiteter + </ns0:OrganizationName> + <ns0:OrganizationDisplayName xml:lang="en">Rolands Identiteter + </ns0:OrganizationDisplayName> + <ns0:OrganizationURL xml:lang="en">http://www.example.com + </ns0:OrganizationURL> + </ns0:Organization> + <ns0:ContactPerson contactType="technical"> + <ns0:GivenName>Roland</ns0:GivenName> + <ns0:SurName>Hedberg</ns0:SurName> + <ns0:EmailAddress>technical@example.com</ns0:EmailAddress> + </ns0:ContactPerson> + <ns0:ContactPerson contactType="support"> + <ns0:GivenName>Support</ns0:GivenName> + <ns0:EmailAddress>support@example.com</ns0:EmailAddress> + </ns0:ContactPerson> +</ns0:EntityDescriptor> diff --git a/tests/md_data.py b/tests/md_data.py index d5176b5a..1a18ce6d 100644 --- a/tests/md_data.py +++ b/tests/md_data.py @@ -1,18 +1,5 @@ #!/usr/bin/env python # -# Copyright (C) 2007 SIOS Technology, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Test data for md""" diff --git a/tests/saml2_data.py b/tests/saml2_data.py index a5f98416..fe650e7b 100644 --- a/tests/saml2_data.py +++ b/tests/saml2_data.py @@ -1,18 +1,5 @@ #!/usr/bin/env python # -# Copyright (C) 2007 SIOS Technology, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Test data for saml2""" diff --git a/tests/samlp_data.py b/tests/samlp_data.py index 22ce0a82..f458d15c 100644 --- a/tests/samlp_data.py +++ b/tests/samlp_data.py @@ -1,18 +1,5 @@ #!/usr/bin/env python # -# Copyright (C) 2007 SIOS Technology, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Test data for saml2""" diff --git a/tests/server_conf_syslog.py b/tests/server_conf_syslog.py index 72e6a748..4dc6a85b 100644 --- a/tests/server_conf_syslog.py +++ b/tests/server_conf_syslog.py @@ -3,48 +3,50 @@ __author__ = 'rolandh' from pathutils import full_path -CONFIG={ - "entityid" : "urn:mace:example.com:saml:roland:sp", - "name" : "urn:mace:example.com:saml:roland:sp", +CONFIG = { + "entityid": "urn:mace:example.com:saml:roland:sp", + "name": "urn:mace:example.com:saml:roland:sp", "description": "My own SP", "service": { "sp": { - "endpoints":{ - "assertion_consumer_service": ["http://lingon.catalogix.se:8087/"], + "endpoints": { + "assertion_consumer_service": [ + "http://lingon.catalogix.se:8087/"], }, "required_attributes": ["surName", "givenName", "mail"], "optional_attributes": ["title"], "idp": ["urn:mace:example.com:saml:roland:idp"], } }, - "debug" : 1, - "key_file" : full_path("test.key"), - "cert_file" : full_path("test.pem"), - #"xmlsec_binary" : None, + "debug": 1, + "key_file": full_path("test.key"), + "cert_file": full_path("test.pem"), + # "xmlsec_binary" : None, "metadata": { "local": [full_path("idp.xml"), full_path("vo_metadata.xml")], }, - "virtual_organization" : { - "urn:mace:example.com:it:tek":{ - "nameid_format" : "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID", + "virtual_organization": { + "urn:mace:example.com:it:tek": { + "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID", "common_identifier": "umuselin", } }, "subject_data": full_path("subject_data.db"), "accepted_time_diff": 60, - "attribute_map_dir" : full_path("attributemaps"), + "attribute_map_dir": full_path("attributemaps"), "organization": { "name": ("AB Exempel", "se"), "display_name": ("AB Exempel", "se"), "url": "http://www.example.org", }, "contact_person": [{ - "given_name": "Roland", - "sur_name": "Hedberg", - "telephone_number": "+46 70 100 0000", - "email_address": ["tech@eample.com", "tech@example.org"], - "contact_type": "technical" - }, + "given_name": "Roland", + "sur_name": "Hedberg", + "telephone_number": "+46 70 100 0000", + "email_address": ["tech@eample.com", + "tech@example.org"], + "contact_type": "technical" + }, ], "logger": { "syslog": { diff --git a/tests/sp_slo_redirect_conf.py b/tests/sp_slo_redirect_conf.py index 7856c11e..97751dae 100644 --- a/tests/sp_slo_redirect_conf.py +++ b/tests/sp_slo_redirect_conf.py @@ -6,16 +6,16 @@ from pathutils import full_path HOME = "http://lingon.catalogix.se:8087/" CONFIG = { - "entityid" : "urn:mace:example.com:saml:roland:sp", - "name" : "urn:mace:example.com:saml:roland:sp", + "entityid": "urn:mace:example.com:saml:roland:sp", + "name": "urn:mace:example.com:saml:roland:sp", "description": "My own SP", "service": { "sp": { - "endpoints":{ + "endpoints": { "assertion_consumer_service": [ - (HOME, BINDING_HTTP_REDIRECT)], - "single_logout_service" : [ - (HOME+"slo",BINDING_HTTP_REDIRECT)], + (HOME, BINDING_HTTP_REDIRECT)], + "single_logout_service": [ + (HOME + "slo", BINDING_HTTP_REDIRECT)], }, "required_attributes": ["surName", "givenName", "mail"], "optional_attributes": ["title"], @@ -23,32 +23,33 @@ CONFIG = { "subject_data": full_path("subject_data.db"), } }, - "debug" : 1, - "key_file" : full_path("test.key"), - "cert_file" : full_path("test.pem"), - "xmlsec_binary" : None, + "debug": 1, + "key_file": full_path("test.key"), + "cert_file": full_path("test.pem"), + "xmlsec_binary": None, "metadata": { "local": [full_path("idp_slo_redirect.xml")], }, - "virtual_organization" : { - "urn:mace:example.com:it:tek":{ - "nameid_format" : "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID", + "virtual_organization": { + "urn:mace:example.com:it:tek": { + "nameid_format": "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID", "common_identifier": "umuselin", } }, "accepted_time_diff": 60, - "attribute_map_dir" : full_path("attributemaps"), + "attribute_map_dir": full_path("attributemaps"), "organization": { "name": ("AB Exempel", "se"), "display_name": ("AB Exempel", "se"), "url": "http://www.example.org", }, "contact_person": [{ - "given_name": "Roland", - "sur_name": "Hedberg", - "telephone_number": "+46 70 100 0000", - "email_address": ["tech@eample.com", "tech@example.org"], - "contact_type": "technical" - }, + "given_name": "Roland", + "sur_name": "Hedberg", + "telephone_number": "+46 70 100 0000", + "email_address": ["tech@eample.com", + "tech@example.org"], + "contact_type": "technical" + }, ] } diff --git a/tests/test_00_xmldsig.py b/tests/test_00_xmldsig.py index 8ebd6ef8..8b40e9db 100644 --- a/tests/test_00_xmldsig.py +++ b/tests/test_00_xmldsig.py @@ -1,18 +1,5 @@ #!/usr/bin/env python # -# Copyright (C) 2007 SIOS Technology, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Tests for xmldsig""" diff --git a/tests/test_02_saml.py b/tests/test_02_saml.py index fdc362b4..41beabee 100644 --- a/tests/test_02_saml.py +++ b/tests/test_02_saml.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2010 Umeå University. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Tests for saml2.saml""" diff --git a/tests/test_04_samlp.py b/tests/test_04_samlp.py index 2cfd59b7..dae76535 100644 --- a/tests/test_04_samlp.py +++ b/tests/test_04_samlp.py @@ -1,19 +1,6 @@ #!/usr/bin/env pythony # -*- coding: utf-8 -*- # -# Copyright (C) 2009 Umeå University. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Tests for saml2.samlp""" diff --git a/tests/test_05_md.py b/tests/test_05_md.py index f0a8ef03..c02d668d 100644 --- a/tests/test_05_md.py +++ b/tests/test_05_md.py @@ -1,19 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (C) 2009 Umeå University. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """Tests for saml2.md""" diff --git a/tests/test_12_s_utils.py b/tests/test_12_s_utils.py index f4cfdda8..730ffa76 100644 --- a/tests/test_12_s_utils.py +++ b/tests/test_12_s_utils.py @@ -1,37 +1,37 @@ -#!/usr/bin/env python +# !/usr/bin/env python # -*- coding: utf-8 -*- -import zlib import base64 -import gzip -from saml2 import make_instance from saml2 import s_utils as utils from saml2 import saml from saml2 import samlp -from saml2 import md from saml2.s_utils import do_attribute_statement - -from saml2.sigver import make_temp - -from saml2.saml import Attribute, NAME_FORMAT_URI, AttributeValue +from saml2.saml import Attribute +from saml2.saml import NAME_FORMAT_URI from py.test import raises from pathutils import full_path -SUCCESS_STATUS = """<?xml version=\'1.0\' encoding=\'UTF-8\'?> -<ns0:Status xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></ns0:Status>""" +SUCCESS_STATUS = ('<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n' +'<ns0:Status xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:StatusCode ' +'Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></ns0:Status>') -ERROR_STATUS = """<?xml version='1.0' encoding='UTF-8'?> -<ns0:Status xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder"><ns0:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal" /></ns0:StatusCode><ns0:StatusMessage>Error resolving principal</ns0:StatusMessage></ns0:Status>""" +ERROR_STATUS = ('<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n' +'<ns0:Status xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:StatusCode ' +'Value="urn:oasis:names:tc:SAML:2.0:status:Responder"><ns0:StatusCode ' +'Value="urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal" ' +'/></ns0:StatusCode><ns0:StatusMessage>Error resolving ' +'principal</ns0:StatusMessage></ns0:Status>') -def _eq(l1,l2): +def _eq(l1, l2): return set(l1) == set(l2) -def _oeq(l1,l2): + +def _oeq(l1, l2): if len(l1) != len(l2): print "Different number of items" return False @@ -42,40 +42,45 @@ def _oeq(l1,l2): print "\t%s" % (ite,) return False return True - + + def test_inflate_then_deflate(): - str = """Selma Lagerlöf (1858-1940) was born in Östra Emterwik, Värmland, + txt = """Selma Lagerlöf (1858-1940) was born in Östra Emterwik, Värmland, Sweden. She was brought up on Mårbacka, the family estate, which she did not leave until 1881, when she went to a teachers' college at Stockholm""" - - interm = utils.deflate_and_base64_encode(str) - bis = utils.decode_base64_and_inflate(interm) - assert bis == str - + + interm = utils.deflate_and_base64_encode(txt) + bis = utils.decode_base64_and_inflate(interm) + assert bis == txt + + def test_status_success(): status = utils.success_status_factory() status_text = "%s" % status assert status_text == SUCCESS_STATUS assert status.status_code.value == samlp.STATUS_SUCCESS - + + def test_error_status(): status = utils.status_message_factory("Error resolving principal", - samlp.STATUS_UNKNOWN_PRINCIPAL, - samlp.STATUS_RESPONDER) - + samlp.STATUS_UNKNOWN_PRINCIPAL, + samlp.STATUS_RESPONDER) + status_text = "%s" % status print status_text assert status_text == ERROR_STATUS + def test_status_from_exception(): e = utils.UnknownPrincipal("Error resolving principal") stat = utils.error_status_factory(e) status_text = "%s" % stat print status_text assert status_text == ERROR_STATUS - + + def test_attribute_sn(): - attr = utils.do_attributes({"surName":("Jeter", "")}) + attr = utils.do_attributes({"surName": ("Jeter", "")}) assert len(attr) == 1 print attr inst = attr[0] @@ -84,9 +89,10 @@ def test_attribute_sn(): av = inst.attribute_value[0] assert av.text == "Jeter" + def test_attribute_age(): - attr = utils.do_attributes({"age":(37, "")}) - + attr = utils.do_attributes({"age": (37, "")}) + assert len(attr) == 1 inst = attr[0] print inst @@ -96,9 +102,10 @@ def test_attribute_age(): assert av.text == "37" assert av.get_type() == "xs:integer" + def test_attribute_onoff(): - attr = utils.do_attributes({"onoff":(False, "")}) - + attr = utils.do_attributes({"onoff": (False, "")}) + assert len(attr) == 1 inst = attr[0] print inst @@ -108,10 +115,11 @@ def test_attribute_onoff(): assert av.text == "false" assert av.get_type() == "xs:boolean" + def test_attribute_base64(): b64sl = base64.b64encode("Selma Lagerlöf") - attr = utils.do_attributes({"name":(b64sl, "xs:base64Binary")}) - + attr = utils.do_attributes({"name": (b64sl, "xs:base64Binary")}) + assert len(attr) == 1 inst = attr[0] print inst @@ -120,18 +128,19 @@ def test_attribute_base64(): av = inst.attribute_value[0] assert av.get_type() == "xs:base64Binary" assert av.text.strip() == b64sl - + + def test_attribute_statement(): - statement = do_attribute_statement({"surName":("Jeter", ""), - "givenName":("Derek", "")}) + statement = do_attribute_statement({"surName": ("Jeter", ""), + "givenName": ("Derek", "")}) print statement assert statement.keyswv() == ["attribute"] assert len(statement.attribute) == 2 attr0 = statement.attribute[0] - assert _eq(attr0.keyswv(), ["name","attribute_value"]) + assert _eq(attr0.keyswv(), ["name", "attribute_value"]) assert len(attr0.attribute_value) == 1 attr1 = statement.attribute[1] - assert _eq(attr1.keyswv(), ["name","attribute_value"]) + assert _eq(attr1.keyswv(), ["name", "attribute_value"]) assert len(attr1.attribute_value) == 1 if attr0.name == "givenName": assert attr0.attribute_value[0].text == "Derek" @@ -143,54 +152,62 @@ def test_attribute_statement(): assert attr1.name == "givenName" assert attr1.attribute_value[0].text == "Derek" + def test_audience(): aud_restr = utils.factory(saml.AudienceRestriction, - audience=utils.factory(saml.Audience,text="urn:foo:bar")) - + audience=utils.factory(saml.Audience, + text="urn:foo:bar")) + assert aud_restr.keyswv() == ["audience"] assert aud_restr.audience.text == "urn:foo:bar" - + + def test_conditions(): conditions = utils.factory(saml.Conditions, - not_before="2009-10-30T07:58:10.852Z", - not_on_or_after="2009-10-30T08:03:10.852Z", - audience_restriction=[utils.factory(saml.AudienceRestriction, - audience=utils.factory(saml.Audience, - text="urn:foo:bar"))]) - + not_before="2009-10-30T07:58:10.852Z", + not_on_or_after="2009-10-30T08:03:10.852Z", + audience_restriction=[ + utils.factory(saml.AudienceRestriction, + audience=utils.factory( + saml.Audience, + text="urn:foo:bar"))]) + assert _eq(conditions.keyswv(), ["not_before", "not_on_or_after", - "audience_restriction"]) - assert conditions.not_before == "2009-10-30T07:58:10.852Z" + "audience_restriction"]) + assert conditions.not_before == "2009-10-30T07:58:10.852Z" assert conditions.not_on_or_after == "2009-10-30T08:03:10.852Z" assert conditions.audience_restriction[0].audience.text == "urn:foo:bar" - + + def test_value_1(): #FriendlyName="givenName" Name="urn:oid:2.5.4.42" # NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" attribute = utils.factory(saml.Attribute, name="urn:oid:2.5.4.42", - name_format=NAME_FORMAT_URI) - assert _eq(attribute.keyswv(),["name","name_format"]) + name_format=NAME_FORMAT_URI) + assert _eq(attribute.keyswv(), ["name", "name_format"]) assert attribute.name == "urn:oid:2.5.4.42" assert attribute.name_format == saml.NAME_FORMAT_URI + def test_value_2(): attribute = utils.factory(saml.Attribute, name="urn:oid:2.5.4.42", - name_format=NAME_FORMAT_URI, - friendly_name="givenName") - assert _eq(attribute.keyswv(),["name","name_format","friendly_name"]) + name_format=NAME_FORMAT_URI, + friendly_name="givenName") + assert _eq(attribute.keyswv(), ["name", "name_format", "friendly_name"]) assert attribute.name == "urn:oid:2.5.4.42" assert attribute.name_format == NAME_FORMAT_URI assert attribute.friendly_name == "givenName" + def test_value_3(): - attribute = utils.factory(saml.Attribute, - attribute_value=[utils.factory( - saml.AttributeValue, text="Derek")], - name="urn:oid:2.5.4.42", - name_format=NAME_FORMAT_URI, - friendly_name="givenName") - - assert _eq(attribute.keyswv(),["name", "name_format", + attribute = utils.factory(saml.Attribute, + attribute_value=[utils.factory( + saml.AttributeValue, text="Derek")], + name="urn:oid:2.5.4.42", + name_format=NAME_FORMAT_URI, + friendly_name="givenName") + + assert _eq(attribute.keyswv(), ["name", "name_format", "friendly_name", "attribute_value"]) assert attribute.name == "urn:oid:2.5.4.42" assert attribute.name_format == NAME_FORMAT_URI @@ -198,43 +215,46 @@ def test_value_3(): assert len(attribute.attribute_value) == 1 assert attribute.attribute_value[0].text == "Derek" + def test_value_4(): - attribute = utils.factory(saml.Attribute, - attribute_value=[utils.factory( - saml.AttributeValue, text="Derek")], - friendly_name="givenName") + attribute = utils.factory(saml.Attribute, + attribute_value=[utils.factory( + saml.AttributeValue, text="Derek")], + friendly_name="givenName") - assert _eq(attribute.keyswv(),["friendly_name", "attribute_value"]) + assert _eq(attribute.keyswv(), ["friendly_name", "attribute_value"]) assert attribute.friendly_name == "givenName" assert len(attribute.attribute_value) == 1 assert attribute.attribute_value[0].text == "Derek" + def test_do_attribute_statement_0(): - statement = do_attribute_statement({"vo_attr":("foobar", "")}) + statement = do_attribute_statement({"vo_attr": ("foobar", "")}) assert statement.keyswv() == ["attribute"] assert len(statement.attribute) == 1 attr0 = statement.attribute[0] - assert _eq(attr0.keyswv(), ["name","attribute_value"]) + assert _eq(attr0.keyswv(), ["name", "attribute_value"]) assert attr0.name == "vo_attr" assert len(attr0.attribute_value) == 1 assert attr0.attribute_value[0].text == "foobar" + def test_do_attribute_statement(): - statement = do_attribute_statement({"surName":("Jeter", ""), - "givenName":(["Derek", - "Sanderson"], "")}) + statement = do_attribute_statement({"surName": ("Jeter", ""), + "givenName": (["Derek", + "Sanderson"], "")}) assert statement.keyswv() == ["attribute"] assert len(statement.attribute) == 2 attr0 = statement.attribute[0] - assert _eq(attr0.keyswv(), ["name","attribute_value"]) + assert _eq(attr0.keyswv(), ["name", "attribute_value"]) attr1 = statement.attribute[1] - assert _eq(attr1.keyswv(), ["name","attribute_value"]) + assert _eq(attr1.keyswv(), ["name", "attribute_value"]) if attr0.name == "givenName": assert len(attr0.attribute_value) == 2 assert _eq([av.text for av in attr0.attribute_value], - ["Derek","Sanderson"]) + ["Derek", "Sanderson"]) assert attr1.name == "surName" assert attr1.attribute_value[0].text == "Jeter" assert len(attr1.attribute_value) == 1 @@ -245,211 +265,234 @@ def test_do_attribute_statement(): assert attr1.name == "givenName" assert len(attr1.attribute_value) == 2 assert _eq([av.text for av in attr1.attribute_value], - ["Derek","Sanderson"]) - + ["Derek", "Sanderson"]) + + def test_do_attribute_statement_multi(): statement = do_attribute_statement( - {( "urn:oid:1.3.6.1.4.1.5923.1.1.1.7", - "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", - "eduPersonEntitlement"):("Jeter", "")}) + {("urn:oid:1.3.6.1.4.1.5923.1.1.1.7", + "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", + "eduPersonEntitlement"): ("Jeter", "")}) assert statement.keyswv() == ["attribute"] assert len(statement.attribute) assert _eq(statement.attribute[0].keyswv(), - ["name","name_format","friendly_name","attribute_value"]) + ["name", "name_format", "friendly_name", "attribute_value"]) attribute = statement.attribute[0] assert attribute.name == "urn:oid:1.3.6.1.4.1.5923.1.1.1.7" assert attribute.name_format == ( - "urn:oasis:names:tc:SAML:2.0:attrname-format:uri") + "urn:oasis:names:tc:SAML:2.0:attrname-format:uri") assert attribute.friendly_name == "eduPersonEntitlement" + def test_subject(): - subject = utils.factory(saml.Subject, text="_aaa", + subject = utils.factory(saml.Subject, text="_aaa", name_id=saml.NameID( - text=saml.NAMEID_FORMAT_TRANSIENT)) + text=saml.NAMEID_FORMAT_TRANSIENT)) - assert _eq(subject.keyswv(),["text", "name_id"]) + assert _eq(subject.keyswv(), ["text", "name_id"]) assert subject.text == "_aaa" assert subject.name_id.text == saml.NAMEID_FORMAT_TRANSIENT + # --------------------------------------------------------------------------- def test_parse_attribute_map(): - (forward, backward) = utils.parse_attribute_map([full_path("attribute.map")]) - + (forward, backward) = utils.parse_attribute_map( + [full_path("attribute.map")]) + assert _eq(forward.keys(), backward.values()) assert _eq(forward.values(), backward.keys()) print forward.keys() assert _oeq(forward.keys(), [ - ('urn:oid:1.3.6.1.4.1.5923.1.1.1.7', NAME_FORMAT_URI), - ('urn:oid:0.9.2342.19200300.100.1.1', NAME_FORMAT_URI), - ('urn:oid:1.3.6.1.4.1.5923.1.1.1.1', NAME_FORMAT_URI), - ('urn:oid:2.5.4.42', NAME_FORMAT_URI), - ('urn:oid:2.5.4.4', NAME_FORMAT_URI), - ('urn:oid:0.9.2342.19200300.100.1.3', NAME_FORMAT_URI), - ('urn:oid:2.5.4.12', NAME_FORMAT_URI)]) + ('urn:oid:1.3.6.1.4.1.5923.1.1.1.7', NAME_FORMAT_URI), + ('urn:oid:0.9.2342.19200300.100.1.1', NAME_FORMAT_URI), + ('urn:oid:1.3.6.1.4.1.5923.1.1.1.1', NAME_FORMAT_URI), + ('urn:oid:2.5.4.42', NAME_FORMAT_URI), + ('urn:oid:2.5.4.4', NAME_FORMAT_URI), + ('urn:oid:0.9.2342.19200300.100.1.3', NAME_FORMAT_URI), + ('urn:oid:2.5.4.12', NAME_FORMAT_URI)]) assert _eq(forward.keys(), [ - ('urn:oid:1.3.6.1.4.1.5923.1.1.1.7', NAME_FORMAT_URI), - ('urn:oid:0.9.2342.19200300.100.1.1', NAME_FORMAT_URI), - ('urn:oid:1.3.6.1.4.1.5923.1.1.1.1', NAME_FORMAT_URI), - ('urn:oid:2.5.4.42', NAME_FORMAT_URI), - ('urn:oid:2.5.4.4', NAME_FORMAT_URI), - ('urn:oid:0.9.2342.19200300.100.1.3', NAME_FORMAT_URI), - ('urn:oid:2.5.4.12', NAME_FORMAT_URI)]) - assert _eq(backward.keys(),["surName","givenName","title","uid","mail", - "eduPersonAffiliation", - "eduPersonEntitlement"]) - + ('urn:oid:1.3.6.1.4.1.5923.1.1.1.7', NAME_FORMAT_URI), + ('urn:oid:0.9.2342.19200300.100.1.1', NAME_FORMAT_URI), + ('urn:oid:1.3.6.1.4.1.5923.1.1.1.1', NAME_FORMAT_URI), + ('urn:oid:2.5.4.42', NAME_FORMAT_URI), + ('urn:oid:2.5.4.4', NAME_FORMAT_URI), + ('urn:oid:0.9.2342.19200300.100.1.3', NAME_FORMAT_URI), + ('urn:oid:2.5.4.12', NAME_FORMAT_URI)]) + assert _eq(backward.keys(), ["surName", "givenName", "title", "uid", "mail", + "eduPersonAffiliation", + "eduPersonEntitlement"]) + def test_identity_attribute_0(): - (forward, backward) = utils.parse_attribute_map([full_path("attribute.map")]) + (forward, backward) = utils.parse_attribute_map( + [full_path("attribute.map")]) a = Attribute(name="urn:oid:2.5.4.4", name_format=NAME_FORMAT_URI, - friendly_name="surName") - - assert utils.identity_attribute("name",a,forward) == "urn:oid:2.5.4.4" - assert utils.identity_attribute("friendly",a,forward) == "surName" - + friendly_name="surName") + + assert utils.identity_attribute("name", a, forward) == "urn:oid:2.5.4.4" + assert utils.identity_attribute("friendly", a, forward) == "surName" + + def test_identity_attribute_1(): - (forward, backward) = utils.parse_attribute_map([full_path("attribute.map")]) + (forward, backward) = utils.parse_attribute_map( + [full_path("attribute.map")]) a = Attribute(name="urn:oid:2.5.4.4", name_format=NAME_FORMAT_URI) - - assert utils.identity_attribute("name",a,forward) == "urn:oid:2.5.4.4" - assert utils.identity_attribute("friendly",a,forward) == "surName" + + assert utils.identity_attribute("name", a, forward) == "urn:oid:2.5.4.4" + assert utils.identity_attribute("friendly", a, forward) == "surName" + def test_identity_attribute_2(): - (forward, backward) = utils.parse_attribute_map([full_path("attribute.map")]) + (forward, backward) = utils.parse_attribute_map( + [full_path("attribute.map")]) a = Attribute(name="urn:oid:2.5.4.5", name_format=NAME_FORMAT_URI) - - assert utils.identity_attribute("name",a,forward) == "urn:oid:2.5.4.5" + + assert utils.identity_attribute("name", a, forward) == "urn:oid:2.5.4.5" # if there would be a map it would be serialNumber - assert utils.identity_attribute("friendly",a,forward) == "urn:oid:2.5.4.5" + assert utils.identity_attribute("friendly", a, forward) == "urn:oid:2.5.4.5" + def test_identity_attribute_3(): a = Attribute(name="urn:oid:2.5.4.5", name_format=NAME_FORMAT_URI) - - assert utils.identity_attribute("name",a) == "urn:oid:2.5.4.5" + + assert utils.identity_attribute("name", a) == "urn:oid:2.5.4.5" # if there would be a map it would be serialNumber - assert utils.identity_attribute("friendly",a) == "urn:oid:2.5.4.5" + assert utils.identity_attribute("friendly", a) == "urn:oid:2.5.4.5" + def test_identity_attribute_4(): a = Attribute(name="urn:oid:2.5.4.5", name_format=NAME_FORMAT_URI, - friendly_name="serialNumber") - - assert utils.identity_attribute("name",a) == "urn:oid:2.5.4.5" + friendly_name="serialNumber") + + assert utils.identity_attribute("name", a) == "urn:oid:2.5.4.5" # if there would be a map it would be serialNumber - assert utils.identity_attribute("friendly",a) == "serialNumber" - -def _givenName(a): + assert utils.identity_attribute("friendly", a) == "serialNumber" + + +def given_name(a): assert a["name"] == "urn:oid:2.5.4.42" assert a["friendly_name"] == "givenName" assert len(a["attribute_value"]) == 1 - assert a["attribute_value"] == [{"text":"Derek"}] + assert a["attribute_value"] == [{"text": "Derek"}] -def _surName(a): + +def sur_name(a): assert a["name"] == "urn:oid:2.5.4.4" assert a["friendly_name"] == "surName" assert len(a["attribute_value"]) == 1 - assert a["attribute_value"] == [{"text":"Jeter"}] - + assert a["attribute_value"] == [{"text": "Jeter"}] + + def test_nameformat_email(): assert utils.valid_email("foo@example.com") assert utils.valid_email("a@b.com") assert utils.valid_email("a@b.se") - assert utils.valid_email("john@doe@johndoe.com") == False - + assert utils.valid_email("john@doe@johndoe.com") is False + + def test_attribute(): a = utils.factory(saml.Attribute, - friendly_name="eduPersonScopedAffiliation", - name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9", - name_format="urn:oasis:names:tc:SAML:2.0:attrname-format:uri") - - assert _eq(a.keyswv(), ["friendly_name","name", "name_format"]) + friendly_name="eduPersonScopedAffiliation", + name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9", + name_format="urn:oasis:names:tc:SAML:2.0:attrname" + "-format:uri") - a = utils.factory(saml.Attribute, - friendly_name="eduPersonScopedAffiliation", + assert _eq(a.keyswv(), ["friendly_name", "name", "name_format"]) + + a = utils.factory( + saml.Attribute, friendly_name="eduPersonScopedAffiliation", name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9", name_format="urn:oasis:names:tc:SAML:2.0:attrname-format:uri", attribute_value=[saml.AttributeValue(text="member@example.com")]) - - assert _eq(a.keyswv(), ["friendly_name","name", "name_format", + + assert _eq(a.keyswv(), ["friendly_name", "name", "name_format", "attribute_value"]) - -def test_attribute_statement(): - statement = utils.factory( saml.Statement, - attribute=[ - utils.factory(saml.Attribute, - attribute_value=[ - utils.factory( - saml.AttributeValue,text="Derek")], - friendly_name="givenName"), - utils.factory(saml.Attribute, - attribute_value=[ - utils.factory( - saml.AttributeValue,text="Jeter")], - friendly_name="surName"), - ]) + + +def test_attribute_statement_2(): + statement = utils.factory(saml.Statement, + attribute=[ + utils.factory(saml.Attribute, + attribute_value=[ + utils.factory( + saml.AttributeValue, + text="Derek")], + friendly_name="givenName"), + utils.factory(saml.Attribute, + attribute_value=[ + utils.factory( + saml.AttributeValue, + text="Jeter")], + friendly_name="surName"), + ]) assert statement.keyswv() == ["attribute"] assert len(statement.attribute) == 2 - + + def test_subject_confirmation_data(): - s = utils.factory( saml.SubjectConfirmation, - in_response_to="_12345678", - not_before="2010-02-11T07:30:00Z", - not_on_or_after="2010-02-11T07:35:00Z", - recipient="http://example.com/sp/", - address="192.168.0.10") - - assert _eq(s.keyswv(),["in_response_to","not_before","not_on_or_after", - "recipient", "address"]) - + s = utils.factory(saml.SubjectConfirmation, + in_response_to="_12345678", + not_before="2010-02-11T07:30:00Z", + not_on_or_after="2010-02-11T07:35:00Z", + recipient="http://example.com/sp/", + address="192.168.0.10") + + assert _eq(s.keyswv(), ["in_response_to", "not_before", "not_on_or_after", + "recipient", "address"]) + + def test_subject_confirmation(): - s = utils.factory( saml.SubjectConfirmation, - method="urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser", - base_id="1234", - name_id="abcd", - subject_confirmation_data=utils.factory( - saml.SubjectConfirmationData, - in_response_to="_1234567890", - recipient="http://example.com/sp/")) + s = utils.factory(saml.SubjectConfirmation, + method="urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser", + base_id="1234", + name_id="abcd", + subject_confirmation_data=utils.factory( + saml.SubjectConfirmationData, + in_response_to="_1234567890", + recipient="http://example.com/sp/")) assert _eq(s.keyswv(), - ["method","base_id","name_id","subject_confirmation_data"]) + ["method", "base_id", "name_id", "subject_confirmation_data"]) assert s.method == "urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser" - + def test_authn_context_class_ref(): - a = utils.factory( saml.AuthnContextClassRef, - text="urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified") + a = utils.factory(saml.AuthnContextClassRef, + text="urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified") assert a.keyswv() == ["text"] assert a.text == "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified" - + + def test_authn_context(): - accr = utils.factory( saml.AuthnContext, - text="urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified") + accr = utils.factory( + saml.AuthnContext, + text="urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified") a = utils.factory(saml.AuthnContext, authn_context_class_ref=accr) assert a.keyswv() == ["authn_context_class_ref"] - + + def test_authn_statement(): - accr = utils.factory( saml.AuthnContextClassRef, - text="urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified") - ac = utils.factory( saml.AuthnContext, - authn_context_class_ref=accr) - ast = utils.factory( saml.AuthnStatement, + accr = utils.factory( + saml.AuthnContextClassRef, + text="urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified") + ac = utils.factory(saml.AuthnContext, + authn_context_class_ref=accr) + ast = utils.factory(saml.AuthnStatement, authn_instant="2010-03-10T12:33:00Z", session_index="_12345", session_not_on_or_after="2010-03-11T12:00:00Z", - authn_context=ac - ) - assert _eq(ast.keyswv(),["authn_instant","session_index", - "session_not_on_or_after", - "authn_context"]) - + authn_context=ac) + assert _eq(ast.keyswv(), ["authn_instant", "session_index", + "session_not_on_or_after", + "authn_context"]) + + def test_signature(): arr = ["foobar", "1234567890"] csum = utils.signature("abcdef", arr) arr.append(csum) - - assert utils.verify_signature("abcdef", arr) - + assert utils.verify_signature("abcdef", arr) diff --git a/tests/test_51_client.py b/tests/test_51_client.py index 11b5cd09..6b93e455 100644 --- a/tests/test_51_client.py +++ b/tests/test_51_client.py @@ -593,6 +593,6 @@ class TestClientWithDummy(): # tc.test_response() if __name__ == "__main__": - tc = TestClient() + tc = TestClientWithDummy() tc.setup_class() - tc.test_sign_then_encrypt_assertion2() + tc.test_do_attribute_query() diff --git a/tests/test_67_manage_name_id.py b/tests/test_67_manage_name_id.py index 7c1dc748..f0e41fb5 100644 --- a/tests/test_67_manage_name_id.py +++ b/tests/test_67_manage_name_id.py @@ -55,7 +55,7 @@ def test_flow(): print _req.message - mnir = idp.create_manage_name_id_response(_req.message, None) + mnir = idp.create_manage_name_id_response(_req.message, [binding]) if binding != BINDING_SOAP: binding, destination = idp.pick_binding("manage_name_id_service", |