diff --git a/README b/README
index 16180a8..a1206c3 100644
--- a/README
+++ b/README
@@ -311,6 +311,13 @@ MELLON_METADATA_HTTP_TIMEOUT
Timeout in seconds for HTTP call made to retrieve metadata files. Default is 10
seconds.
+MELLON_ASSERTION_CONSUMER_BINDINGS
+----------------------------------
+
+The list of supported assertion consumer bindings. Default is::
+
+ ['post', 'artifact']
+
Tests
=====
diff --git a/mellon/app_settings.py b/mellon/app_settings.py
index a177abf..103efea 100644
--- a/mellon/app_settings.py
+++ b/mellon/app_settings.py
@@ -26,6 +26,7 @@ class AppSettings:
'CREATE_GROUP': True,
'ERROR_URL': None,
'ERROR_REDIRECT_AFTER_TIMEOUT': 120,
+ 'ASSERTION_CONSUMER_BINDINGS': ['post', 'artifact'],
'DEFAULT_ASSERTION_CONSUMER_BINDING': 'post', # or artifact
'VERIFY_SSL_CERTIFICATE': True,
'OPENED_SESSION_COOKIE_NAME': None,
diff --git a/mellon/templates/mellon/metadata.xml b/mellon/templates/mellon/metadata.xml
index 333f98e..ceaaaa1 100644
--- a/mellon/templates/mellon/metadata.xml
+++ b/mellon/templates/mellon/metadata.xml
@@ -32,21 +32,21 @@
{% for name_id_format in name_id_formats %}
{{ name_id_format }}
{% endfor %}
-
-
-
+{% endif %}
{% if organization and organization.NAMES and organization.DISPLAY_NAMES and organization.URLS %}
{% for name in organization.NAMES %}
diff --git a/mellon/utils.py b/mellon/utils.py
index a05fcae..ef9d8f7 100644
--- a/mellon/utils.py
+++ b/mellon/utils.py
@@ -54,6 +54,7 @@ def create_metadata(request):
'logout_url': request.build_absolute_uri(logout_url),
'public_keys': public_keys,
'name_id_formats': name_id_formats,
+ 'assertion_consumer_bindings': app_settings.ASSERTION_CONSUMER_BINDINGS,
'default_assertion_consumer_binding': app_settings.DEFAULT_ASSERTION_CONSUMER_BINDING,
'organization': app_settings.ORGANIZATION,
'contact_persons': app_settings.CONTACT_PERSONS,
diff --git a/mellon/views.py b/mellon/views.py
index aac7a75..660624c 100644
--- a/mellon/views.py
+++ b/mellon/views.py
@@ -29,7 +29,7 @@ from django.conf import settings
from django.contrib import auth
from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model
from django.db import transaction
-from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect
+from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseRedirect
from django.shortcuts import render, resolve_url
from django.urls import reverse
from django.utils import six
@@ -163,8 +163,12 @@ class LoginView(ProfileMixin, LogMixin, View):
def post(self, request, *args, **kwargs):
'''Assertion consumer'''
if 'SAMLart' in request.POST:
+ if 'artifact' not in app_settings.ASSERTION_CONSUMER_BINDINGS:
+ raise Http404('artifact binding is not supported')
return self.continue_sso_artifact(request, lasso.HTTP_METHOD_ARTIFACT_POST)
if 'SAMLResponse' not in request.POST:
+ if 'post' not in app_settings.ASSERTION_CONSUMER_BINDINGS:
+ raise Http404('post binding is not supported')
return self.get(request, *args, **kwargs)
if not utils.is_nonnull(request.POST['SAMLResponse']):
return HttpResponseBadRequest('SAMLResponse contains a null character')
@@ -462,6 +466,8 @@ class LoginView(ProfileMixin, LogMixin, View):
def get(self, request, *args, **kwargs):
'''Initialize login request'''
if 'SAMLart' in request.GET:
+ if 'artifact' not in app_settings.ASSERTION_CONSUMER_BINDINGS:
+ raise Http404('artifact binding is not supported')
return self.continue_sso_artifact(request, lasso.HTTP_METHOD_ARTIFACT_GET)
# redirect to discovery service if needed
diff --git a/tests/test_utils.py b/tests/test_utils.py
index d4f38cb..718779a 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -112,6 +112,48 @@ def test_create_metadata(rf, private_settings, caplog):
namespaces=ns,
)
+ private_settings.MELLON_ASSERTION_CONSUMER_BINDINGS = ['post']
+ with mock.patch('mellon.utils.open', mock.mock_open(read_data='BEGIN\nyyy\nEND'), create=True):
+ metadata = create_metadata(request)
+ assert 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
+ assert_xml_constraints(
+ metadata.encode('utf-8'),
+ (
+ '/sm:EntityDescriptor/sm:SPSSODescriptor',
+ 1,
+ (
+ '/sm:AssertionConsumerService[@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\']',
+ 0,
+ ),
+ (
+ '/sm:AssertionConsumerService[@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\']',
+ 1,
+ ),
+ ),
+ namespaces=ns,
+ )
+
+ private_settings.MELLON_ASSERTION_CONSUMER_BINDINGS = ['artifact']
+ with mock.patch('mellon.utils.open', mock.mock_open(read_data='BEGIN\nyyy\nEND'), create=True):
+ metadata = create_metadata(request)
+ assert 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
+ assert_xml_constraints(
+ metadata.encode('utf-8'),
+ (
+ '/sm:EntityDescriptor/sm:SPSSODescriptor',
+ 1,
+ (
+ '/sm:AssertionConsumerService[@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact\']',
+ 1,
+ ),
+ (
+ '/sm:AssertionConsumerService[@Binding=\'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\']',
+ 0,
+ ),
+ ),
+ namespaces=ns,
+ )
+
def test_iso8601_to_datetime(private_settings):
import django.utils.timezone