diff --git a/mellon/app_settings.py b/mellon/app_settings.py
index cb3de72..ecece59 100644
--- a/mellon/app_settings.py
+++ b/mellon/app_settings.py
@@ -30,6 +30,8 @@ class AppSettings(object):
'VERIFY_SSL_CERTIFICATE': True,
'OPENED_SESSION_COOKIE_NAME': None,
'OPENED_SESSION_COOKIE_DOMAIN': None,
+ 'ORGANIZATION': None,
+ 'CONTACT_PERSONS': [],
}
@property
diff --git a/mellon/templates/mellon/metadata.xml b/mellon/templates/mellon/metadata.xml
index b5051e6..633907f 100644
--- a/mellon/templates/mellon/metadata.xml
+++ b/mellon/templates/mellon/metadata.xml
@@ -36,5 +36,51 @@
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="{{ login_url }}" />
+ {% if organization and organization.NAMES and organization.DISPLAY_NAMES and organization.URLS %}
+
+ {% for name in organization.NAMES %}
+ {% if name.LABEL %}
+ {{ name.LABEL }}
+ {% else %}
+ {{ name }}
+ {% endif %}
+ {% endfor %}
+ {% for display_name in organization.DISPLAY_NAMES %}
+ {% if display_name.LABEL %}
+ {{ display_name.LABEL }}
+ {% else %}
+ {{ display_name }}
+ {% endif %}
+ {% endfor %}
+ {% for url in organization.URLS %}
+ {% if url.URL %}
+ {{ url.URL }}
+ {% else %}
+ {{ url }}
+ {% endif %}
+ {% endfor %}
+
+ {% endif %}
+ {% for contact_person in contact_persons %}
+ {% if contact_person.CONTACT_TYPE %}
+
+ {% if contact_person.COMPANY %}
+ {{ contact_person.COMPANY }}
+ {% endif %}
+ {% if contact_person.GIVEN_NAME %}
+ {{ contact_person.GIVEN_NAME }}
+ {% endif %}
+ {% if contact_person.SURNAME %}
+ {{ contact_person.SURNAME }}
+ {% endif %}
+ {% for email_address in contact_person.EMAIL_ADDRESSES %}
+ {{ email_address }}
+ {% endfor %}
+ {% for telephone_number in contact_person.TELEPHONE_NUMBERS %}
+ {{ telephone_number }}
+ {% endfor %}
+
+ {% endif %}
+ {% endfor %}
diff --git a/mellon/utils.py b/mellon/utils.py
index b5f9956..d18a1d2 100644
--- a/mellon/utils.py
+++ b/mellon/utils.py
@@ -36,6 +36,8 @@ def create_metadata(request):
'public_keys': public_keys,
'name_id_formats': name_id_formats,
'default_assertion_consumer_binding': app_settings.DEFAULT_ASSERTION_CONSUMER_BINDING,
+ 'organization': app_settings.ORGANIZATION,
+ 'contact_persons': app_settings.CONTACT_PERSONS,
})
settings._MELLON_METADATA_CACHE = cache
return settings._MELLON_METADATA_CACHE[entity_id]
diff --git a/tests/conftest.py b/tests/conftest.py
index 9c3c8a2..9bdb738 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -19,3 +19,16 @@ def concurrency(settings):
return 20
else:
return 100
+
+
+@pytest.fixture
+def private_settings(request):
+ import django.conf
+ from django.conf import UserSettingsHolder
+ old = django.conf.settings._wrapped
+ django.conf.settings._wrapped = UserSettingsHolder(old)
+
+ def finalizer():
+ django.conf.settings._wrapped = old
+ request.addfinalizer(finalizer)
+ return django.conf.settings
diff --git a/tests/test_views.py b/tests/test_views.py
index 7d8deef..deb9edd 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -1,4 +1,110 @@
+import mock
+import lasso
+
from django.core.urlresolvers import reverse
+from xml_utils import assert_xml_constraints
+
+
def test_null_character_on_samlresponse_post(app):
app.post(reverse('mellon_login'), {'SAMLResponse': '\x00'}, status=400)
+
+
+def test_metadata(private_settings, client):
+ ns = {
+ 'sm': 'urn:oasis:names:tc:SAML:2.0:metadata',
+ 'ds': 'http://www.w3.org/2000/09/xmldsig#',
+ }
+ private_settings.MELLON_PUBLIC_KEYS = ['xxx', '/yyy']
+ private_settings.MELLON_NAME_ID_FORMATS = [lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED]
+ private_settings.MELLON_DEFAULT_ASSERTION_CONSUMER_BINDING = 'artifact'
+ private_settings.MELLON_ORGANIZATION = {
+ 'NAMES': [
+ 'Foobar',
+ {
+ 'LABEL': 'FoobarEnglish',
+ 'LANG': 'en',
+ }
+ ],
+ 'DISPLAY_NAMES': [
+ 'Foobar',
+ {
+ 'LABEL': 'FoobarEnglish',
+ 'LANG': 'en',
+ }
+ ],
+ 'URLS': [
+ 'http://foobar.com/',
+ {
+ 'URL': 'http://foobar.com/en/',
+ 'LANG': 'en',
+ }
+ ],
+ }
+ private_settings.MELLON_CONTACT_PERSONS = [
+ {
+ 'CONTACT_TYPE': 'administrative',
+ 'COMPANY': 'FooBar',
+ 'GIVENNAME': 'John',
+ 'SURNAME': 'Doe',
+ 'EMAIL_ADDRESSES': [
+ 'john.doe@foobar.com',
+ 'john.doe@personal-email.com',
+ ],
+ 'TELEPHONE_NUMBERS': [
+ '+abcd',
+ '+1234',
+ ],
+ },
+ {
+ 'CONTACT_TYPE': 'technical',
+ 'COMPANY': 'FooBar2',
+ 'GIVENNAME': 'John',
+ 'SURNAME': 'Doe',
+ 'EMAIL_ADDRESSES': [
+ 'john.doe@foobar.com',
+ 'john.doe@personal-email.com',
+ ],
+ 'TELEPHONE_NUMBERS': [
+ '+abcd',
+ '+1234',
+ ],
+ },
+ ]
+
+ with mock.patch('mellon.utils.file', mock.mock_open(read_data='BEGIN\nyyy\nEND'), create=True):
+ response = client.get('/metadata/')
+ assert_xml_constraints(
+ response.content,
+ ('/sm:EntityDescriptor[@entityID="http://testserver/metadata/"]', 1,
+ ('/*', 4),
+ ('/sm:SPSSODescriptor', 1,
+ ('/*', 6),
+ ('/sm:NameIDFormat', 1),
+ ('/sm:SingleLogoutService', 1),
+ ('/sm:AssertionConsumerService', None,
+ ('[@isDefault="true"]', None,
+ ('[@Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"]', 1),
+ ('[@Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"]', 0)),
+ ('[@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))),
+ ('/sm:Organization', 1,
+ ('/sm:OrganizationName', 2),
+ ('/sm:OrganizationName[text()="Foobar"]', 1),
+ ('/sm:OrganizationName[text()="FoobarEnglish"]', 1,
+ ('[@xml:lang="en"]', 1)),
+ ('/sm:OrganizationDisplayName', 2),
+ ('/sm:OrganizationDisplayName[text()="Foobar"]', 1),
+ ('/sm:OrganizationDisplayName[text()="FoobarEnglish"]', 1,
+ ('[@xml:lang="en"]', 1)),
+ ('/sm:OrganizationURL', 2),
+ ('/sm:OrganizationURL[text()="http://foobar.com/"]', 1),
+ ('/sm:OrganizationURL[text()="http://foobar.com/en/"]', 1,
+ ('[@xml:lang="en"]', 1))),
+ ('/sm:ContactPerson', 2,
+ ('[@contactType="technical"]', 1),
+ ('[@contactType="administrative"]', 1))),
+ namespaces=ns)