From cd2f6592c3575df5a98060fedd1b60347c4f360f Mon Sep 17 00:00:00 2001
From: Adeel Khan <muhammad.adeel@arbisoft.com>
Date: Mon, 16 Apr 2018 16:57:45 +0500
Subject: [PATCH] Catch HttpError thrown by Python Social Auth.

This patch enables catching HttpError exception raised
by PSA because of any issue caused at client/server.
Further it redirects user to login page with a
message about the error.

LEARNER-4344
---
 .../djangoapps/third_party_auth/middleware.py | 25 +++++++++++
 .../third_party_auth/tests/test_middleware.py | 42 +++++++++++++++++++
 2 files changed, 67 insertions(+)
 create mode 100644 common/djangoapps/third_party_auth/tests/test_middleware.py

diff --git a/common/djangoapps/third_party_auth/middleware.py b/common/djangoapps/third_party_auth/middleware.py
index 81e4d3de786..19993ad64dd 100644
--- a/common/djangoapps/third_party_auth/middleware.py
+++ b/common/djangoapps/third_party_auth/middleware.py
@@ -1,7 +1,16 @@
 """Middleware classes for third_party_auth."""
 
+import urlparse
+
+from django.contrib import messages
+from django.shortcuts import redirect
+from django.urls import reverse
+from django.utils.translation import ugettext as _
+from requests import HTTPError
 from social_django.middleware import SocialAuthExceptionMiddleware
 
+from student.helpers import get_next_url_for_login_page
+
 from . import pipeline
 
 
@@ -23,3 +32,19 @@ class ExceptionMiddleware(SocialAuthExceptionMiddleware):
             redirect_uri = pipeline.AUTH_DISPATCH_URLS[auth_entry]
 
         return redirect_uri
+
+    def process_exception(self, request, exception):
+        """Handles specific exception raised by Python Social Auth eg HTTPError."""
+
+        referer_url = request.META.get('HTTP_REFERER', '')
+        if (referer_url and isinstance(exception, HTTPError) and
+                exception.response.status_code == 502):
+            referer_url = urlparse.urlparse(referer_url).path
+            if referer_url == reverse('signin_user'):
+                messages.error(request, _('Unable to connect with the external provider, please try again'),
+                               extra_tags='social-auth')
+
+                redirect_url = get_next_url_for_login_page(request)
+                return redirect('/login?next=' + redirect_url)
+
+        return super(ExceptionMiddleware, self).process_exception(request, exception)
diff --git a/common/djangoapps/third_party_auth/tests/test_middleware.py b/common/djangoapps/third_party_auth/tests/test_middleware.py
new file mode 100644
index 00000000000..88757b264af
--- /dev/null
+++ b/common/djangoapps/third_party_auth/tests/test_middleware.py
@@ -0,0 +1,42 @@
+"""
+Tests for third party auth middleware
+"""
+import mock
+from django.contrib.messages.middleware import MessageMiddleware
+from django.http import HttpResponse
+from django.test.client import RequestFactory
+from requests.exceptions import HTTPError
+
+from openedx.core.djangolib.testing.utils import skip_unless_lms
+from third_party_auth.middleware import ExceptionMiddleware
+from third_party_auth.tests.testutil import TestCase
+from student.helpers import get_next_url_for_login_page
+
+
+class ThirdPartyAuthMiddlewareTestCase(TestCase):
+    """Tests that ExceptionMiddleware is correctly redirected"""
+
+    @skip_unless_lms
+    @mock.patch('django.conf.settings.MESSAGE_STORAGE', 'django.contrib.messages.storage.cookie.CookieStorage')
+    def test_http_exception_redirection(self):
+        """
+        Test ExceptionMiddleware is correctly redirected to login page
+        when PSA raises HttpError exception.
+        """
+
+        request = RequestFactory().get("dummy_url")
+        next_url = get_next_url_for_login_page(request)
+        login_url = '/login?next=' + next_url
+        request.META['HTTP_REFERER'] = 'http://example.com:8000/login'
+        exception = HTTPError()
+        exception.response = HttpResponse(status=502)
+
+        # Add error message for error in auth pipeline
+        MessageMiddleware().process_request(request)
+        response = ExceptionMiddleware().process_exception(
+            request, exception
+        )
+        target_url = response.url
+
+        self.assertEqual(response.status_code, 302)
+        self.assertTrue(target_url.endswith(login_url))
-- 
GitLab