Commit e76e05fa by Nimisha Asthagiri

Specially handle login redirect for mobile apps

parent f3cb6924
......@@ -2706,3 +2706,8 @@ MAX_BOOKMARKS_PER_COURSE = 100
# lms.env.json file.
REGISTRATION_EXTENSION_FORM = None
# Identifier included in the User Agent from open edX mobile apps.
MOBILE_APP_USER_AGENT_REGEXES = [
r'edX/org.edx.mobile',
]
......@@ -61,10 +61,12 @@ from django.contrib.auth import SESSION_KEY
from django.contrib.auth.views import redirect_to_login
from django.contrib.sessions.middleware import SessionMiddleware
from django.core import signing
from django.http import HttpResponse
from django.utils.crypto import get_random_string
from hashlib import sha256
from logging import getLogger
from openedx.core.lib.mobile_utils import is_request_from_mobile_app
log = getLogger(__name__)
......@@ -339,6 +341,12 @@ class SafeSessionMiddleware(SessionMiddleware):
cookie and redirects the user to the login page.
"""
_mark_cookie_for_deletion(request)
# Mobile apps have custom handling of authentication failures. They
# should *not* be redirected to the website's login page.
if is_request_from_mobile_app(request):
return HttpResponse(status=401)
return redirect_to_login(request.path)
@staticmethod
......
......@@ -10,6 +10,7 @@ from django.contrib.auth.models import AnonymousUser
from django.http import HttpResponse, HttpResponseRedirect, SimpleCookie
from django.test import TestCase
from django.test.client import RequestFactory
from django.test.utils import override_settings
from mock import patch
from student.tests.factories import UserFactory
......@@ -18,6 +19,17 @@ from ..middleware import SafeSessionMiddleware, SafeCookieData
from .test_utils import TestSafeSessionsLogMixin
def create_mock_request():
"""
Creates and returns a mock request object for testing.
"""
request = RequestFactory()
request.COOKIES = {}
request.META = {}
request.path = '/'
return request
class TestSafeSessionProcessRequest(TestSafeSessionsLogMixin, TestCase):
"""
Test class for SafeSessionMiddleware.process_request
......@@ -25,9 +37,7 @@ class TestSafeSessionProcessRequest(TestSafeSessionsLogMixin, TestCase):
def setUp(self):
super(TestSafeSessionProcessRequest, self).setUp()
self.user = UserFactory.create()
self.request = RequestFactory()
self.request.COOKIES = {}
self.request.path = '/'
self.request = create_mock_request()
def assert_response(self, safe_cookie_data=None, success=True):
"""
......@@ -128,9 +138,7 @@ class TestSafeSessionProcessResponse(TestSafeSessionsLogMixin, TestCase):
def setUp(self):
super(TestSafeSessionProcessResponse, self).setUp()
self.user = UserFactory.create()
self.request = RequestFactory()
self.request.COOKIES = {}
self.request.path = '/'
self.request = create_mock_request()
self.request.session = {}
self.client.response = HttpResponse()
self.client.response.cookies = SimpleCookie()
......@@ -230,9 +238,7 @@ class TestSafeSessionMiddleware(TestSafeSessionsLogMixin, TestCase):
def setUp(self):
super(TestSafeSessionMiddleware, self).setUp()
self.user = UserFactory.create()
self.request = RequestFactory()
self.request.COOKIES = {}
self.request.path = '/'
self.request = create_mock_request()
self.client.response = HttpResponse()
self.client.response.cookies = SimpleCookie()
......@@ -246,7 +252,10 @@ class TestSafeSessionMiddleware(TestSafeSessionsLogMixin, TestCase):
settings.SESSION_COOKIE_NAME
]
def test_success(self):
def verify_success(self):
"""
Verifies success path.
"""
self.client.login(username=self.user.username, password='test')
self.request.user = self.user
......@@ -265,11 +274,27 @@ class TestSafeSessionMiddleware(TestSafeSessionsLogMixin, TestCase):
response = SafeSessionMiddleware().process_response(self.request, self.client.response)
self.assertEquals(response.status_code, 200)
def test_error(self):
def test_success(self):
self.verify_success()
def test_success_from_mobile_web_view(self):
self.request.path = '/xblock/block-v1:org+course+run+type@html+block@block_id'
self.verify_success()
@override_settings(MOBILE_APP_USER_AGENT_REGEXES=[r'open edX Mobile App'])
def test_success_from_mobile_app(self):
self.request.META = {'HTTP_USER_AGENT': 'open edX Mobile App Version 2.1'}
self.verify_success()
def verify_error(self, expected_response_status):
"""
Verifies error path.
"""
self.request.COOKIES[settings.SESSION_COOKIE_NAME] = 'not-a-safe-cookie'
with self.assert_parse_error():
SafeSessionMiddleware().process_request(self.request)
request_response = SafeSessionMiddleware().process_request(self.request)
self.assertEquals(request_response.status_code, expected_response_status)
self.assertTrue(self.request.need_to_delete_cookie)
self.cookies_from_request_to_response()
......@@ -277,3 +302,15 @@ class TestSafeSessionMiddleware(TestSafeSessionsLogMixin, TestCase):
with patch('django.http.HttpResponse.set_cookie') as mock_delete_cookie:
SafeSessionMiddleware().process_response(self.request, self.client.response)
self.assertTrue(mock_delete_cookie.called)
def test_error(self):
self.verify_error(302)
def test_error_from_mobile_web_view(self):
self.request.path = '/xblock/block-v1:org+course+run+type@html+block@block_id'
self.verify_error(401)
@override_settings(MOBILE_APP_USER_AGENT_REGEXES=[r'open edX Mobile App'])
def test_error_from_mobile_app(self):
self.request.META = {'HTTP_USER_AGENT': 'open edX Mobile App Version 2.1'}
self.verify_error(401)
"""
Common utilities related to the mobile apps.
"""
import re
from django.conf import settings
def is_request_from_mobile_app(request):
"""
Returns whether the given request was made by an open edX mobile app,
either natively or through the mobile web view.
Note: The check for the user agent works only for mobile apps version 2.1
and higher. Previous apps did not update their user agents to include the
distinguishing string.
The check for the web view is a temporary check that works for mobile apps
version 2.0 and higher. See is_request_from_mobile_web_view for more
information.
Args:
request (HttpRequest)
"""
if is_request_from_mobile_web_view(request):
return True
if getattr(settings, 'MOBILE_APP_USER_AGENT_REGEXES', None):
user_agent = request.META.get('HTTP_USER_AGENT')
if user_agent:
for user_agent_regex in settings.MOBILE_APP_USER_AGENT_REGEXES:
if re.match(user_agent_regex, user_agent):
return True
return False
PATHS_ACCESSED_BY_MOBILE_WITH_SESSION_COOKIES = [
r'^/xblock/{usage_key_string}$'.format(usage_key_string=settings.USAGE_KEY_PATTERN),
]
def is_request_from_mobile_web_view(request):
"""
Returns whether the given request was made by an open edX mobile web
view using a session cookie.
Args:
request (HttpRequest)
"""
# TODO (MA-1825): This is a TEMPORARY HACK until all of the version 2.0
# iOS mobile apps have updated. The earlier versions didn't update their
# user agents so we are checking for the specific URLs that are
# accessed through the mobile web view.
for mobile_path in PATHS_ACCESSED_BY_MOBILE_WITH_SESSION_COOKIES:
if re.match(mobile_path, request.path):
return True
return False
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