Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,18 @@
# .. toggle_target_removal_date: 2021-10-01
# .. toggle_tickets: 'https://openedx.atlassian.net/browse/MICROBA-1405'
'ENABLE_V2_CERT_DISPLAY_SETTINGS': False,

# .. toggle_name: FEATURES['ENABLE_REDIRECT_UNAUTHENTICATED_USERS_TO_LOGIN']
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
# .. toggle_description: Enable this feature to redirect all unauthenticated users to login page. Used to make all
# content private, where when it's disabled, there are some pages which can be viewed by unauthenticated users.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2021-11-12
# .. toggle_target_removal_date: None
# .. toggle_warnings: None
# .. toggle_tickets: 'https://github.com/open-craft/edx-platform/pull/439'
'ENABLE_REDIRECT_UNAUTHENTICATED_USERS_TO_LOGIN': False,
}

# Specifies extra XBlock fields that should available when requested via the Course Blocks API
Expand Down Expand Up @@ -2082,6 +2094,8 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring
#'django.contrib.auth.middleware.AuthenticationMiddleware',
'openedx.core.djangoapps.cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',

'openedx.core.djangoapps.user_authn.middleware.RedirectUnauthenticatedToLoginMiddleware',

'common.djangoapps.student.middleware.UserStandingMiddleware',
'openedx.core.djangoapps.contentserver.middleware.StaticContentServer',

Expand Down
60 changes: 60 additions & 0 deletions openedx/core/djangoapps/user_authn/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Middleware for User Authentication
"""


from django.conf import settings
from django.http import QueryDict
from django.shortcuts import redirect


class RedirectUnauthenticatedToLoginMiddleware:
"""
Middleware that redirects unauthenticated users to login page.

Any GET request comming from an unauthenticated user will be responded with
a redirect to the login url. The middleware ignores requests to login and
register pages.

If redirects, passes the requested url to login as 'next' query string
parameter.

To enable the middleware, ENABLE_REDIRECT_UNAUTHENTICATED_USERS_TO_LOGIN
setting has to be set to True.

Assumed that the requests passed to the middleware have user attribute set.
"""

def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
if self._should_redirect(request):
return redirect(settings.LOGIN_URL + '?' + self._get_redirect_query_string(request))

return self.get_response(request)

def _should_redirect(self, request):
"""
Determines if a request should be redirected to login.
"""
if not settings.FEATURES.get('ENABLE_REDIRECT_UNAUTHENTICATED_USERS_TO_LOGIN', False):
return False

is_get_request = request.method == 'GET'
is_login_or_register_url = request.path in (settings.LOGIN_URL, '/register')

return (
is_get_request and
(not is_login_or_register_url) and
(not request.user.is_authenticated)
)

def _get_redirect_query_string(self, request):
"""
Generates query string for redirect.
"""
# calling copy to get mutable QueryDict
query = QueryDict().copy()
query['next'] = request.get_full_path()
return query.urlencode()
122 changes: 122 additions & 0 deletions openedx/core/djangoapps/user_authn/tests/test_middlewares.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# pylint: disable=missing-docstring


import ddt
from django.conf import settings
from django.http import QueryDict
from django.test import RequestFactory, TestCase
from unittest.mock import Mock, patch
from urllib.parse import urlparse

from common.djangoapps.student.tests.factories import AnonymousUserFactory, UserFactory
from openedx.core.djangoapps.user_authn.middleware import RedirectUnauthenticatedToLoginMiddleware


@ddt.ddt
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_REDIRECT_UNAUTHENTICATED_USERS_TO_LOGIN": True})
class RedirectUnauthenticatedToLoginMiddlewareTests(TestCase):
"""
Tests for RedirectUnauthenticatedToLoginMiddleware.
"""

def setUp(self):
super().setUp()
self.mock_response = Mock()
self.middleware = RedirectUnauthenticatedToLoginMiddleware(
lambda request: self.mock_response
)

@ddt.data(
RequestFactory().head('/'),
RequestFactory().post('/'),
RequestFactory().put('/'),
RequestFactory().options('/'),
RequestFactory().delete('/'),
)
def test_does_not_redirect_non_GET_requests(self, request):
"""
Middleware doesn't redirect non GET requests.
"""
request.user = AnonymousUserFactory.create()

response = self.middleware(request)

self.assertEqual(response, self.mock_response)

def test_redirects_unauthenticated_user_to_login(self):
"""
Middleware redirects unauthenticated user to login page.
"""
request = RequestFactory().get('/')
request.user = AnonymousUserFactory.create()

response = self.middleware(request)

self.assertNotEqual(response, self.mock_response)
path = urlparse(response.url).path
self.assertEqual(path, settings.LOGIN_URL)

def test_passes_url_in_next_query_string(self):
"""
Middleware passes url in 'next' query string parameter.

When redirecting, the middleware should add 'next' query string
parameter, that contains the url that was originally requested.
"""
request = RequestFactory().get('/dashboard?test=123')
request.user = AnonymousUserFactory.create()

response = self.middleware(request)

self.assertNotEqual(response, self.mock_response)
query = QueryDict(urlparse(response.url).query)
self.assertIn('next', query)
self.assertEqual(query['next'], '/dashboard?test=123')

def test_does_not_redirect_if_user_is_authenticated(self):
"""
Middleware doesn't redirect authenticated users.
"""
request = RequestFactory().get('/')
request.user = UserFactory.create()

response = self.middleware(request)

self.assertEqual(response, self.mock_response)

def test_get_login_does_not_redirect_unauthenticated_user(self):
"""
Middleware doesn't redirect unauthenticated user visiting login page.
"""
request = RequestFactory().get(settings.LOGIN_URL)
request.user = AnonymousUserFactory.create()

response = self.middleware(request)

self.assertEqual(response, self.mock_response)

def test_get_register_does_not_redirect_unauthenticated_user(self):
"""
Middleware doesn't redirect unauthenticated user visiting register page.
"""
request = RequestFactory().get('/register')
request.user = AnonymousUserFactory.create()

response = self.middleware(request)

self.assertEqual(response, self.mock_response)

@patch.dict("django.conf.settings.FEATURES", {"ENABLE_REDIRECT_UNAUTHENTICATED_USERS_TO_LOGIN": False})
def test_does_not_redirect_unauthenticated_user_if_setting_disabled(self):
"""
Middleware doesn't redirect if settings is set to False.

If ENABLE_REDIRECT_UNAUTHENTICATED_USERS_TO_LOGIN setting is set to
False, the middleware should not redirect unauthenticated users.
"""
request = RequestFactory().get('/')
request.user = AnonymousUserFactory.create()

response = self.middleware(request)

self.assertEqual(response, self.mock_response)