Commit 9d217f5d by zubair-arbi

ENT-768

display friendly error message for saml rejection
parent 257de20d
......@@ -279,7 +279,8 @@ def simulate_running_pipeline(pipeline_target, backend, email=None, fullname=Non
pipeline_data = {
"backend": backend,
"kwargs": {
"details": kwargs
"details": kwargs,
"response": kwargs.get("response", {})
}
}
......
......@@ -12,13 +12,16 @@ import pytest
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.contrib.messages.middleware import MessageMiddleware
from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse
from django.http import HttpRequest
from django.test import TestCase
from django.test.client import RequestFactory
from django.test.utils import override_settings
from django.utils.translation import ugettext as _
from edx_oauth2_provider.tests.factories import AccessTokenFactory, ClientFactory, RefreshTokenFactory
from edx_rest_api_client import exceptions
from http.cookies import SimpleCookie
......@@ -33,6 +36,7 @@ from course_modes.models import CourseMode
from lms.djangoapps.commerce.models import CommerceConfiguration
from lms.djangoapps.commerce.tests import factories
from lms.djangoapps.commerce.tests.mocks import mock_get_orders
from lms.djangoapps.student_account.views import login_and_registration_form
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
......@@ -452,6 +456,114 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
expected_ec
)
def _configure_testshib_provider(self, provider_name, idp_slug):
"""
Enable and configure the TestShib SAML IdP as a third_party_auth provider.
"""
kwargs = {}
kwargs.setdefault('name', provider_name)
kwargs.setdefault('enabled', True)
kwargs.setdefault('visible', True)
kwargs.setdefault('idp_slug', idp_slug)
kwargs.setdefault('entity_id', 'https://idp.testshib.org/idp/shibboleth')
kwargs.setdefault('metadata_source', 'https://mock.testshib.org/metadata/testshib-providers.xml')
kwargs.setdefault('icon_class', 'fa-university')
kwargs.setdefault('attr_email', 'dummy-email-attr')
kwargs.setdefault('max_session_length', None)
self.configure_saml_provider(**kwargs)
@mock.patch('django.conf.settings.MESSAGE_STORAGE', 'django.contrib.messages.storage.cookie.CookieStorage')
@mock.patch('lms.djangoapps.student_account.views.enterprise_customer_for_request')
@ddt.data(
(
'signin_user',
'tpa-saml',
'TestShib',
{
'name': 'FakeName',
'logo': 'https://host.com/logo.jpg',
'welcome_msg': 'No message'
}
)
)
@ddt.unpack
def test_saml_auth_with_error(
self,
url_name,
current_backend,
current_provider,
expected_enterprise_customer_mock_attrs,
enterprise_customer_mock,
):
params = []
request = RequestFactory().get(reverse(url_name), params, HTTP_ACCEPT='text/html')
self.enable_saml()
dummy_idp = 'testshib'
self._configure_testshib_provider(current_provider, dummy_idp)
request.session = {}
request.user = AnonymousUser()
expected_ec = mock.MagicMock(
branding_configuration=mock.MagicMock(
logo=mock.MagicMock(
url=expected_enterprise_customer_mock_attrs['logo']
),
welcome_message=expected_enterprise_customer_mock_attrs['welcome_msg']
)
)
expected_ec.name = expected_enterprise_customer_mock_attrs['name']
enterprise_customer_data = {
'uuid': '72416e52-8c77-4860-9584-15e5b06220fb',
'name': 'Dummy Enterprise',
'identity_provider': dummy_idp,
}
enterprise_customer_mock.return_value = enterprise_customer_data
dummy_error_message = 'Authentication failed: SAML login failed ' \
'["invalid_response"] [SAML Response must contain 1 assertion]'
# Add error message for error in auth pipeline
request.COOKIES = {}
MessageMiddleware().process_request(request)
messages.error(request, dummy_error_message, extra_tags='social-auth')
# Simulate a running pipeline
pipeline_response = {
'response': {
'idp_name': dummy_idp
}
}
pipeline_target = 'student_account.views.third_party_auth.pipeline'
with simulate_running_pipeline(pipeline_target, current_backend, **pipeline_response):
with mock.patch('edxmako.request_context.get_current_request', return_value=request):
response = login_and_registration_form(request)
expected_providers = []
expected_error_message = _(
u'We are sorry, you are not authorized to access {platform_name} '
u'via this channel. Please contact your {enterprise} administrator in '
u'order to access {platform_name} or contact {link_start}edX Support{link_end}.'
u'{line_break}{line_break}'
u'Error Details:{line_break}'
u'{error_message}'.format(
platform_name=settings.PLATFORM_NAME,
enterprise=enterprise_customer_data['name'],
error_message=dummy_error_message,
link_start='<a href="{edx_support_url}">'.format(
edx_support_url=settings.SUPPORT_SITE_LINK
),
link_end='</a>',
line_break='<br/>'
)
)
self._assert_saml_auth_data_with_error(
response,
current_backend,
current_provider,
expected_error_message
)
def test_hinted_login(self):
params = [("next", "/courses/something/?tpa_hint=oa2-google-oauth2")]
response = self.client.get(reverse('signin_user'), params, HTTP_ACCEPT="text/html")
......@@ -649,7 +761,31 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
expected_data = '"third_party_auth": {auth_info}'.format(
auth_info=auth_info
)
self.assertContains(response, expected_data)
def _assert_saml_auth_data_with_error(
self, response, current_backend, current_provider, expected_error_message
):
"""
Verify that third party auth info is rendered correctly in a DOM data attribute.
"""
finish_auth_url = None
if current_backend:
finish_auth_url = reverse('social:complete', kwargs={'backend': current_backend}) + '?'
auth_info = {
'currentProvider': current_provider,
'providers': [],
'secondaryProviders': [],
'finishAuthUrl': finish_auth_url,
'errorMessage': expected_error_message,
'registerFormSubmitButtonText': 'Create Account',
}
auth_info = dump_js_escaped_json(auth_info)
expected_data = '"third_party_auth": {auth_info}'.format(
auth_info=auth_info
)
self.assertContains(response, expected_data)
def _third_party_login_url(self, backend_name, auth_entry, login_params):
......
......@@ -380,6 +380,26 @@ def _third_party_auth_context(request, redirect_to, tpa_hint=None):
context['errorMessage'] = _(unicode(msg)) # pylint: disable=translation-of-non-string
break
if enterprise_customer and context['errorMessage']:
context['errorMessage'] = _(
u'We are sorry, you are not authorized to access {platform_name} via this channel. '
u'Please contact your {enterprise} administrator in order to access {platform_name} '
u'or contact {link_start}edX Support{link_end}.{line_break}'
u'{line_break}'
u'Error Details:{line_break}{error_message}'.format(
platform_name=configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME),
enterprise=enterprise_customer['name'],
error_message=context['errorMessage'],
link_start='<a href="{edx_support_url}">'.format(
edx_support_url=configuration_helpers.get_value(
'SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK
)
),
link_end='</a>',
line_break='<br/>'
)
)
return context
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment