115 lines
5.4 KiB
Python
115 lines
5.4 KiB
Python
""" Test Student helpers """
|
|
|
|
|
|
import logging
|
|
|
|
import ddt
|
|
from django.conf import settings
|
|
from django.contrib.sessions.middleware import SessionMiddleware
|
|
from django.test import TestCase
|
|
from django.test.client import RequestFactory
|
|
from django.test.utils import override_settings
|
|
from mock import patch
|
|
from testfixtures import LogCapture
|
|
|
|
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration_context
|
|
from student.helpers import get_next_url_for_login_page
|
|
|
|
LOGGER_NAME = "student.helpers"
|
|
|
|
|
|
@ddt.ddt
|
|
class TestLoginHelper(TestCase):
|
|
"""Test login helper methods."""
|
|
static_url = settings.STATIC_URL
|
|
|
|
def setUp(self):
|
|
super(TestLoginHelper, self).setUp()
|
|
self.request = RequestFactory()
|
|
|
|
@staticmethod
|
|
def _add_session(request):
|
|
"""Annotate the request object with a session"""
|
|
middleware = SessionMiddleware()
|
|
middleware.process_request(request)
|
|
request.session.save()
|
|
|
|
@ddt.data(
|
|
(logging.WARNING, "WARNING", "https://www.amazon.com", "text/html", None,
|
|
"Unsafe redirect parameter detected after login page: 'https://www.amazon.com'"),
|
|
(logging.WARNING, "WARNING", "testserver/edx.org/images/logo", "text/html", None,
|
|
"Redirect to theme content detected after login page: 'testserver/edx.org/images/logo'"),
|
|
(logging.INFO, "INFO", "favicon.ico", "image/*", "test/agent",
|
|
"Redirect to non html content 'image/*' detected from 'test/agent' after login page: 'favicon.ico'"),
|
|
(logging.WARNING, "WARNING", "https://www.test.com/test.jpg", "image/*", None,
|
|
"Unsafe redirect parameter detected after login page: 'https://www.test.com/test.jpg'"),
|
|
(logging.INFO, "INFO", static_url + "dummy.png", "image/*", "test/agent",
|
|
"Redirect to non html content 'image/*' detected from 'test/agent' after login page: '" + static_url +
|
|
"dummy.png" + "'"),
|
|
(logging.WARNING, "WARNING", "test.png", "text/html", None,
|
|
"Redirect to url path with specified filed type 'image/png' not allowed: 'test.png'"),
|
|
(logging.WARNING, "WARNING", static_url + "dummy.png", "text/html", None,
|
|
"Redirect to url path with specified filed type 'image/png' not allowed: '" + static_url + "dummy.png" + "'"),
|
|
)
|
|
@ddt.unpack
|
|
def test_next_failures(self, log_level, log_name, unsafe_url, http_accept, user_agent, expected_log):
|
|
""" Test unsafe next parameter """
|
|
with LogCapture(LOGGER_NAME, level=log_level) as logger:
|
|
req = self.request.get(settings.LOGIN_URL + "?next={url}".format(url=unsafe_url))
|
|
req.META["HTTP_ACCEPT"] = http_accept # pylint: disable=no-member
|
|
req.META["HTTP_USER_AGENT"] = user_agent # pylint: disable=no-member
|
|
get_next_url_for_login_page(req)
|
|
logger.check(
|
|
(LOGGER_NAME, log_name, expected_log)
|
|
)
|
|
|
|
@ddt.data(
|
|
('/dashboard', 'testserver'),
|
|
('https://edx.org/courses', 'edx.org'),
|
|
('https://test.edx.org/courses', 'edx.org'),
|
|
('https://test2.edx.org/courses', 'edx.org'),
|
|
)
|
|
@ddt.unpack
|
|
@override_settings(LOGIN_REDIRECT_WHITELIST=['test.edx.org', 'test2.edx.org'])
|
|
def test_safe_next(self, next_url, host):
|
|
""" Test safe next parameter """
|
|
req = self.request.get(settings.LOGIN_URL + "?next={url}".format(url=next_url), HTTP_HOST=host)
|
|
req.META["HTTP_ACCEPT"] = "text/html" # pylint: disable=no-member
|
|
next_page = get_next_url_for_login_page(req)
|
|
self.assertEqual(next_page, next_url)
|
|
|
|
@patch('student.helpers.third_party_auth.pipeline.get')
|
|
@ddt.data(
|
|
# Test requests outside the TPA pipeline - tpa_hint should be added.
|
|
(None, '/dashboard', '/dashboard', False),
|
|
('', '/dashboard', '/dashboard', False),
|
|
('', '/dashboard?tpa_hint=oa2-google-oauth2', '/dashboard?tpa_hint=oa2-google-oauth2', False),
|
|
('saml-idp', '/dashboard', '/dashboard?tpa_hint=saml-idp', False),
|
|
# THIRD_PARTY_AUTH_HINT can be overridden via the query string
|
|
('saml-idp', '/dashboard?tpa_hint=oa2-google-oauth2', '/dashboard?tpa_hint=oa2-google-oauth2', False),
|
|
|
|
# Test requests inside the TPA pipeline - tpa_hint should not be added, preventing infinite loop.
|
|
(None, '/dashboard', '/dashboard', True),
|
|
('', '/dashboard', '/dashboard', True),
|
|
('', '/dashboard?tpa_hint=oa2-google-oauth2', '/dashboard?tpa_hint=oa2-google-oauth2', True),
|
|
('saml-idp', '/dashboard', '/dashboard', True),
|
|
# OK to leave tpa_hint overrides in place.
|
|
('saml-idp', '/dashboard?tpa_hint=oa2-google-oauth2', '/dashboard?tpa_hint=oa2-google-oauth2', True),
|
|
)
|
|
@ddt.unpack
|
|
def test_third_party_auth_hint(self, tpa_hint, next_url, expected_url, running_pipeline, mock_running_pipeline):
|
|
mock_running_pipeline.return_value = running_pipeline
|
|
|
|
def validate_login():
|
|
req = self.request.get(settings.LOGIN_URL + "?next={url}".format(url=next_url))
|
|
req.META["HTTP_ACCEPT"] = "text/html" # pylint: disable=no-member
|
|
self._add_session(req)
|
|
next_page = get_next_url_for_login_page(req)
|
|
self.assertEqual(next_page, expected_url)
|
|
|
|
with override_settings(FEATURES=dict(settings.FEATURES, THIRD_PARTY_AUTH_HINT=tpa_hint)):
|
|
validate_login()
|
|
|
|
with with_site_configuration_context(configuration=dict(THIRD_PARTY_AUTH_HINT=tpa_hint)):
|
|
validate_login()
|