diff --git a/mellon/app_settings.py b/mellon/app_settings.py index 33ac3ce..fdbf949 100644 --- a/mellon/app_settings.py +++ b/mellon/app_settings.py @@ -43,6 +43,7 @@ class AppSettings(object): 'LOOKUP_BY_ATTRIBUTES': [], 'METADATA_CACHE_TIME': 3600, 'METADATA_HTTP_TIMEOUT': 10, + 'METADATA_PUBLISH_DISCOVERY_RESPONSE': False, } @property diff --git a/mellon/templates/mellon/metadata.xml b/mellon/templates/mellon/metadata.xml index da01a4f..c67283e 100644 --- a/mellon/templates/mellon/metadata.xml +++ b/mellon/templates/mellon/metadata.xml @@ -6,12 +6,14 @@ AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + {% if discovery_endpoint_url %} - + + {% endif %} {% for public_key in public_keys %} diff --git a/mellon/utils.py b/mellon/utils.py index 5bbd726..b50e475 100644 --- a/mellon/utils.py +++ b/mellon/utils.py @@ -49,7 +49,8 @@ def create_metadata(request): public_key = ''.join(content.splitlines()[1:-1]) public_keys.append(public_key) name_id_formats = app_settings.NAME_ID_FORMATS - return render_to_string('mellon/metadata.xml', { + ctx = { + 'request': request, 'entity_id': request.build_absolute_uri(entity_id), 'login_url': request.build_absolute_uri(login_url), 'logout_url': request.build_absolute_uri(logout_url), @@ -58,8 +59,11 @@ def create_metadata(request): 'default_assertion_consumer_binding': app_settings.DEFAULT_ASSERTION_CONSUMER_BINDING, 'organization': app_settings.ORGANIZATION, 'contact_persons': app_settings.CONTACT_PERSONS, - 'discovery_endpoint_url': request.build_absolute_uri(reverse('mellon_login')), - }) + } + if app_settings.METADATA_PUBLISH_DISCOVERY_RESPONSE: + ctx['discovery_endpoint_url'] = request.build_absolute_uri( + reverse('mellon_login')) + return render_to_string('mellon/metadata.xml', ctx) def create_server(request): diff --git a/tests/test_utils.py b/tests/test_utils.py index 91247bc..832e7ae 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -35,6 +35,28 @@ def test_create_metadata(rf, private_settings, caplog): private_settings.MELLON_NAME_ID_FORMATS = [lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED] private_settings.MELLON_DEFAULT_ASSERTION_CONSUMER_BINDING = 'artifact' request = rf.get('/') + with mock.patch('mellon.utils.open', mock.mock_open(read_data='BEGIN\nyyy\nEND'), create=True): + metadata = create_metadata(request) + assert_xml_constraints( + metadata.encode('utf-8'), + ('/sm:EntityDescriptor[@entityID="http://testserver/metadata/"]', 1, + ('/*', 1), + ('/sm:SPSSODescriptor', 1, + ('/*', 6), + ('/sm:NameIDFormat', 1), + ('/sm:SingleLogoutService', 1), + ('/sm:AssertionConsumerService[@isDefault=\'true\'][@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\']', 1), + ('/sm:AssertionConsumerService[@isDefault=\'true\'][@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\']', + 0), + ('/sm:AssertionConsumerService[@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\']', + 1), + ('/sm:KeyDescriptor/ds:KeyInfo/ds:X509Data', 2, + ('/ds:X509Certificate', 2), + ('/ds:X509Certificate[text()=\'xxx\']', 1), + ('/ds:X509Certificate[text()=\'yyy\']', 1)))), + namespaces=ns) + + private_settings.MELLON_METADATA_PUBLISH_DISCOVERY_RESPONSE = True with mock.patch('mellon.utils.open', mock.mock_open(read_data='BEGIN\nyyy\nEND'), create=True): metadata = create_metadata(request) assert_xml_constraints( diff --git a/tests/test_views.py b/tests/test_views.py index 5bfe079..bf66855 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -112,7 +112,7 @@ def test_metadata(private_settings, client): ('/sm:EntityDescriptor[@entityID="http://testserver/metadata/"]', 1, ('/*', 4), ('/sm:SPSSODescriptor', 1, - ('/*', 7), + ('/*', 6), ('/sm:NameIDFormat', 1), ('/sm:SingleLogoutService', 1), ('/sm:AssertionConsumerService', None,