Studio login/registration redirects to LMS

This commit is contained in:
Nimisha Asthagiri
2018-12-15 16:25:27 -05:00
parent 0858ae233a
commit 886bc4b20b
27 changed files with 128 additions and 145 deletions

View File

@@ -309,7 +309,7 @@ class AuthTestCase(ContentStoreTestCase):
resp = self.client.get_html(course_url)
# re-request, and we should get a redirect to login page
self.assertRedirects(resp, settings.LOGIN_REDIRECT_URL + '?next=/home/')
self.assertRedirects(resp, settings.LOGIN_URL + '?next=/home/')
@mock.patch.dict(settings.FEATURES, {"ALLOW_PUBLIC_ACCOUNT_CREATION": False})
def test_signup_button_index_page(self):

View File

@@ -4,6 +4,7 @@ Public views
from django.conf import settings
from django.template.context_processors import csrf
from django.urls import reverse
from django.utils.http import urlquote_plus
from django.shortcuts import redirect
from django.views.decorators.clickjacking import xframe_options_deny
from django.views.decorators.csrf import ensure_csrf_cookie
@@ -14,7 +15,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
from waffle.decorators import waffle_switch
from contentstore.config import waffle
__all__ = ['signup', 'login_page', 'howitworks', 'accessibility']
__all__ = ['signup', 'login_page', 'login_redirect_to_lms', 'howitworks', 'accessibility']
@ensure_csrf_cookie
@@ -66,6 +67,20 @@ def login_page(request):
)
def login_redirect_to_lms(request):
"""
This view redirects to the LMS login view. It is used for Django's LOGIN_URL
setting, which is where unauthenticated requests to protected endpoints are redirected.
"""
next_url = request.GET.get('next')
absolute_next_url = request.build_absolute_uri(next_url)
login_url = '{base_url}/login{params}'.format(
base_url=settings.LMS_ROOT_URL,
params='?next=' + urlquote_plus(absolute_next_url) if next_url else '',
)
return redirect(login_url)
def howitworks(request):
"Proxy view"
if request.user.is_authenticated:

View File

@@ -148,7 +148,7 @@ MOCK_SEARCH_BACKING_FILE = (
# this secret key should be the same as lms/envs/bok_choy.py's
SECRET_KEY = "very_secret_bok_choy_key"
LMS_ROOT_URL = "http://localhost:8000"
LMS_ROOT_URL = "http://localhost:8003"
if RELEASE_LINE == "master":
# On master, acceptance tests use edX books, not the default Open edX books.
HELP_TOKENS_BOOKS = {

View File

@@ -6,9 +6,10 @@ Settings for Bok Choy tests that are used when running Studio in Docker-based de
# noinspection PyUnresolvedReferences
from .bok_choy import * # pylint: disable=wildcard-import
CMS_BASE = '{}:{}'.format(os.environ['BOK_CHOY_HOSTNAME'], os.environ['BOK_CHOY_CMS_PORT'])
LMS_BASE = '{}:{}'.format(os.environ['BOK_CHOY_HOSTNAME'], os.environ['BOK_CHOY_LMS_PORT'])
CMS_BASE = '{}:{}'.format(os.environ['BOK_CHOY_HOSTNAME'], os.environ.get('BOK_CHOY_CMS_PORT', 8031))
LMS_BASE = '{}:{}'.format(os.environ['BOK_CHOY_HOSTNAME'], os.environ.get('BOK_CHOY_LMS_PORT', 8003))
LMS_ROOT_URL = 'http://{}'.format(LMS_BASE)
LOGIN_REDIRECT_WHITELIST = [CMS_BASE]
COMMENTS_SERVICE_URL = 'http://{}:4567'.format(os.environ['BOK_CHOY_HOSTNAME'])
EDXNOTES_PUBLIC_API = 'http://{}:8042/api/v1'.format(os.environ['BOK_CHOY_HOSTNAME'])

View File

@@ -146,6 +146,7 @@ from lms.envs.common import (
_make_locale_paths,
)
from path import Path as path
from django.core.urlresolvers import reverse_lazy
from lms.djangoapps.lms_xblock.mixin import LmsBlockMixin
from cms.lib.xblock.authoring_mixin import AuthoringMixin
@@ -371,6 +372,7 @@ CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth', # this is required for admin
'django.template.context_processors.csrf',
'help_tokens.context_processor',
'openedx.core.djangoapps.site_configuration.context_processors.configuration_context',
)
# Django templating
@@ -426,9 +428,8 @@ DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0]
##############################################################################
EDX_ROOT_URL = ''
LOGIN_REDIRECT_URL = EDX_ROOT_URL + '/signin'
LOGIN_URL = EDX_ROOT_URL + '/signin'
LOGIN_REDIRECT_URL = EDX_ROOT_URL + '/home/'
LOGIN_URL = reverse_lazy('login_redirect_to_lms')
# use the ratelimit backend to prevent brute force attacks
AUTHENTICATION_BACKENDS = [

View File

@@ -9,8 +9,8 @@ LOGGING['handlers']['local'] = LOGGING['handlers']['tracking'] = {
LOGGING['loggers']['tracking']['handlers'] = ['console']
LMS_BASE = 'edx.devstack.lms:18000'
CMS_BASE = 'edx.devstack.studio:18010'
LMS_BASE = 'localhost:18000'
CMS_BASE = 'localhost:18010'
LMS_ROOT_URL = 'http://{}'.format(LMS_BASE)
FEATURES.update({

View File

@@ -134,6 +134,7 @@ if os.environ.get('DISABLE_MIGRATIONS'):
LMS_BASE = "localhost:8000"
LMS_ROOT_URL = "http://{}".format(LMS_BASE)
FEATURES['PREVIEW_LMS_BASE'] = "preview.localhost"
LOGIN_URL = EDX_ROOT_URL + '/signin'
CACHES = {

View File

@@ -229,6 +229,10 @@
</nav>
% else:
<%
login_url = settings.LMS_ROOT_URL + '/login'
register_url = settings.LMS_ROOT_URL + '/register'
%>
<nav class="nav-not-signedin nav-pitch" aria-label="${_('Account')}">
<h2 class="sr-only">${_("Account Navigation")}</h2>
<ol>
@@ -237,11 +241,11 @@
</li>
% if static.get_value('ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION')):
<li class="nav-item nav-not-signedin-signup">
<a class="action action-signup" href="${reverse('signup')}">${_("Sign Up")}</a>
<a class="action action-signup" href="${register_url}?next=${current_url}">${_("Sign Up")}</a>
</li>
% endif
<li class="nav-item nav-not-signedin-signin">
<a class="action action-signin" href="${reverse('login')}">${_("Sign In")}</a>
<a class="action action-signin" href="${login_url}?next=${current_url}">${_("Sign In")}</a>
</li>
</ol>
</nav>

View File

@@ -39,7 +39,9 @@
</span>
<span class="icon fa fa-caret-down ui-toggle-dd" aria-hidden="true"></span>
</h3>
<%
logout_url = settings.LMS_ROOT_URL + '/logout'
%>
<div class="wrapper wrapper-nav-sub">
<div class="nav-sub">
<ul>
@@ -52,7 +54,7 @@
</li>
% endif
<li class="nav-item nav-account-signout">
<a class="action action-signout" href="${reverse('logout')}">${_("Sign Out")}</a>
<a class="action action-signout" href="${logout_url}?next=${current_site_url}">${_("Sign Out")}</a>
</li>
</ul>
</div>

View File

@@ -83,6 +83,7 @@ urlpatterns = [
url(r'^howitworks$', contentstore.views.howitworks, name='howitworks'),
url(r'^signup$', contentstore.views.signup, name='signup'),
url(r'^signin$', contentstore.views.login_page, name='login'),
url(r'^signin_redirect_to_lms$', contentstore.views.login_redirect_to_lms, name='login_redirect_to_lms'),
url(r'^request_course_creator$', contentstore.views.request_course_creator, name='request_course_creator'),
url(r'^course_team/{}(?:/(?P<email>.+))?$'.format(COURSELIKE_KEY_PATTERN),
contentstore.views.course_team_handler, name='course_team_handler'),

View File

@@ -67,12 +67,8 @@ from openedx.features.journals.api import get_journals_context
from student.forms import AccountCreationForm, PasswordResetFormNoActive, get_registration_extension_form
from student.helpers import (
DISABLE_UNENROLL_CERT_STATES,
auth_pipeline_urls,
cert_info,
create_or_set_user_attribute_created_on_site,
do_create_account,
generate_activation_email_context,
get_next_url_for_login_page
)
from student.message_types import EmailChange, PasswordReset
from student.models import (

View File

@@ -3,4 +3,6 @@ import os
# Get the URL of the instance under test
HOSTNAME = os.environ.get('BOK_CHOY_HOSTNAME', 'localhost')
CMS_PORT = os.environ.get('BOK_CHOY_CMS_PORT', 8031)
LMS_PORT = os.environ.get('BOK_CHOY_LMS_PORT', 8003)
BASE_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, CMS_PORT))
LMS_URL = os.environ.get('test_url', 'http://{}:{}'.format(HOSTNAME, LMS_PORT))

View File

@@ -4,7 +4,7 @@ Login page for Studio.
from bok_choy.page_object import PageObject
from bok_choy.promise import EmptyPromise
from common.test.acceptance.pages.studio import BASE_URL
from common.test.acceptance.pages.studio import LMS_URL
from common.test.acceptance.pages.studio.course_page import CoursePage
from common.test.acceptance.pages.studio.utils import HelpMixin
@@ -13,24 +13,25 @@ class LoginMixin(object):
"""
Mixin class used for logging into the system.
"""
def fill_field(self, css, value):
def fill_password(self, password):
"""
Fill the login form field with the value.
Fill the password field with the value.
"""
self.q(css=css).fill(value)
self.q(css="#login-password").fill(password)
def login(self, email, password, expect_success=True):
"""
Attempt to log in using 'email' and 'password'.
"""
self.fill_field('input#email', email)
self.fill_field('input#password', password)
self.q(css='button#submit').first.click()
self.wait_for_element_visibility('#login-email', 'Email field is shown')
self.q(css="#login-email").fill(email)
self.fill_password(password)
self.q(css=".login-button").click()
# Ensure that we make it to another page
if expect_success:
EmptyPromise(
lambda: "signin" not in self.browser.current_url,
lambda: "login" not in self.browser.current_url,
"redirected from the login page"
).fulfill()
@@ -39,17 +40,20 @@ class LoginPage(PageObject, LoginMixin, HelpMixin):
"""
Login page for Studio.
"""
url = BASE_URL + "/signin"
url = LMS_URL + "/login"
def is_browser_on_page(self):
return self.q(css='body.view-signin').visible
return (
self.q(css="#login-anchor").is_present() and
self.q(css=".login-button").visible
)
class CourseOutlineSignInRedirectPage(CoursePage, LoginMixin):
"""
Page shown when the user tries to accesses the course while not signed in.
Page shown when the user tries to access the course while not signed in.
"""
url_path = "course"
def is_browser_on_page(self):
return self.q(css='body.view-signin').visible
return self.q(css=".login-button").visible

View File

@@ -4,7 +4,7 @@ Signup page for studio
from bok_choy.page_object import PageObject
from common.test.acceptance.pages.common.utils import click_css
from common.test.acceptance.pages.studio import BASE_URL
from common.test.acceptance.pages.studio import LMS_URL
from common.test.acceptance.pages.studio.utils import HelpMixin, set_input_value
@@ -13,22 +13,29 @@ class SignupPage(PageObject, HelpMixin):
Signup page for Studio.
"""
url = BASE_URL + "/signup"
url = LMS_URL + "/register"
def is_browser_on_page(self):
return self.q(css='body.view-signup').visible
return (
self.q(css="#register-anchor").is_present() and
self.q(css=".register-button").visible
)
def input_password(self, password):
"""Inputs a password and then returns the password input"""
return set_input_value(self, '#password', password)
return set_input_value(self, "#register-password", password)
def sign_up_user(self, registration_dictionary):
def sign_up_user(self, email, name, username, password, country="US", favorite_movie="Alf"):
"""
Register the user.
"""
for css, value in registration_dictionary.iteritems():
set_input_value(self, css, value)
self.q(css="#register-email").fill(email)
self.q(css="#register-name").fill(name)
self.q(css="#register-username").fill(username)
self.q(css="#register-password").fill(password)
self.q(css="#register-country").results[0].send_keys(country)
self.q(css="#register-favorite_movie").fill(favorite_movie)
click_css(page=self, css='#tos', require_notification=False)
click_css(page=self, css='#submit', require_notification=False)
self.wait_for_element_absence('#submit', 'Submit button is gone.')
# Submit it
self.q(css=".register-button").click()
self.wait_for_element_absence('.register-button', 'Register button is gone.')

View File

@@ -8,6 +8,7 @@ from selenium.webdriver.common.keys import Keys
from base_studio_test import StudioCourseTest
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
from common.test.acceptance.pages.studio import LMS_URL
from common.test.acceptance.pages.studio.asset_index import AssetIndexPageStudioFrontend
from common.test.acceptance.pages.studio.course_info import CourseUpdatesPage
from common.test.acceptance.pages.studio.edit_tabs import PagesPage
@@ -118,15 +119,14 @@ class SignUpAndSignInTest(UniqueCourseTest):
index_page.visit()
index_page.click_sign_up()
unique_number = uuid.uuid4().hex[:4]
registration_dic = {
'#email': '{}-email@host.com'.format(unique_number),
'#name': '{}-name'.format(unique_number),
'#username': '{}-username'.format(unique_number),
'#password': '{}-password'.format(unique_number),
}
# Register the user.
self.sign_up_page.sign_up_user(registration_dic)
unique_number = uuid.uuid4().hex[:4]
self.sign_up_page.sign_up_user(
'{}-email@host.com'.format(unique_number),
'{}-name'.format(unique_number),
'{}-username'.format(unique_number),
'{}-password'.format(unique_number),
)
home = HomePage(self.browser)
home.wait_for_page()
@@ -145,8 +145,8 @@ class SignUpAndSignInTest(UniqueCourseTest):
password_input = self.sign_up_page.input_password('a') # Arbitrary short password that will fail
password_input.send_keys(Keys.TAB) # Focus out of the element
index_page.wait_for_element_visibility('#password_error', 'Password Error Message')
self.assertIsNotNone(index_page.q(css='#password_error').text) # Make sure there is an error message
index_page.wait_for_element_visibility('#register-password-validation-error', 'Password Error Message')
self.assertIsNotNone(index_page.q(css='#register-password-validation-error-msg')) # Error message should exist
def test_login_with_valid_redirect(self):
"""
@@ -184,9 +184,8 @@ class SignUpAndSignInTest(UniqueCourseTest):
self.browser.get(self.browser.current_url.split('=')[0] + '=http://www.google.com')
# Login
self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password'])
home = HomePage(self.browser)
home.wait_for_page()
self.assertEqual(self.browser.current_url, home.url)
# Verify that we land in LMS instead of the invalid redirect url
self.assertEqual(self.browser.current_url, LMS_URL + "/dashboard")
def test_login_with_mistyped_credentials(self):
"""
@@ -219,16 +218,9 @@ class SignUpAndSignInTest(UniqueCourseTest):
)
# Verify that login error is shown
self.course_outline_sign_in_redirect_page.wait_for_element_visibility(
'#login_error',
".js-form-errors.status.submission-error",
'Login error is visible'
)
# Change the password
self.course_outline_sign_in_redirect_page.fill_field('input#password', 'changed_password')
# Login error should not be visible
self.course_outline_sign_in_redirect_page.wait_for_element_invisibility(
'#login_error',
'Login error is not visible'
)
# Login with correct credentials
self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password'])
self.course_outline_page.wait_for_page()

View File

@@ -84,68 +84,6 @@ class StudioHelpTest(StudioCourseTest):
)
@attr(shard=20)
class SignInHelpTest(AcceptanceTest):
"""
Tests help links on 'Sign In' page
"""
def setUp(self):
super(SignInHelpTest, self).setUp()
self.index_page = IndexPage(self.browser)
self.index_page.visit()
def test_sign_in_nav_help(self):
"""
Scenario: Help link in navigation bar is working on 'Sign In' page.
Given that I am on the 'Sign In" page.
And I want help about the sign in
And I click the 'Help' in the navigation bar
Then Help link should open.
And help url should be correct
"""
sign_in_page = self.index_page.click_sign_in()
expected_url = _get_expected_documentation_url('/getting_started/index.html')
# Assert that help link is correct.
assert_nav_help_link(
test=self,
page=sign_in_page,
href=expected_url,
signed_in=False
)
@attr(shard=20)
class SignUpHelpTest(AcceptanceTest):
"""
Tests help links on 'Sign Up' page.
"""
def setUp(self):
super(SignUpHelpTest, self).setUp()
self.index_page = IndexPage(self.browser)
self.index_page.visit()
def test_sign_up_nav_help(self):
"""
Scenario: Help link in navigation bar is working on 'Sign Up' page.
Given that I am on the 'Sign Up" page.
And I want help about the sign up
And I click the 'Help' in the navigation bar
Then Help link should open.
And help url should be correct
"""
sign_up_page = self.index_page.click_sign_up()
expected_url = _get_expected_documentation_url('/getting_started/index.html')
# Assert that help link is correct.
assert_nav_help_link(
test=self,
page=sign_up_page,
href=expected_url,
signed_in=False
)
@attr(shard=20)
class HomeHelpTest(StudioCourseTest):
"""

View File

@@ -233,7 +233,10 @@ BADGING_BACKEND = 'lms.djangoapps.badges.backends.tests.dummy_backend.DummyBacke
# Configure the LMS to use our stub eCommerce implementation
ECOMMERCE_API_URL = 'http://localhost:8043/api/v2/'
LMS_ROOT_URL = "http://localhost:8000"
LMS_ROOT_URL = "http://localhost:{}".format(os.environ.get('BOK_CHOY_LMS_PORT', 8003))
CMS_BASE = "localhost:{}".format(os.environ.get('BOK_CHOY_CMS_PORT', 8031))
LOGIN_REDIRECT_WHITELIST = [CMS_BASE]
if RELEASE_LINE == "master":
# On master, acceptance tests use edX books, not the default Open edX books.
HELP_TOKENS_BOOKS = {

View File

@@ -6,9 +6,10 @@ Settings for Bok Choy tests that are used when running Studio in Docker-based de
# noinspection PyUnresolvedReferences
from .bok_choy import * # pylint: disable=wildcard-import
CMS_BASE = '{}:{}'.format(os.environ['BOK_CHOY_HOSTNAME'], os.environ['BOK_CHOY_CMS_PORT'])
LMS_BASE = '{}:{}'.format(os.environ['BOK_CHOY_HOSTNAME'], os.environ['BOK_CHOY_LMS_PORT'])
CMS_BASE = '{}:{}'.format(os.environ['BOK_CHOY_HOSTNAME'], os.environ.get('BOK_CHOY_CMS_PORT', 8031))
LMS_BASE = '{}:{}'.format(os.environ['BOK_CHOY_HOSTNAME'], os.environ.get('BOK_CHOY_LMS_PORT', 8003))
LMS_ROOT_URL = 'http://{}'.format(LMS_BASE)
LOGIN_REDIRECT_WHITELIST = [CMS_BASE]
COMMENTS_SERVICE_URL = 'http://{}:4567'.format(os.environ['BOK_CHOY_HOSTNAME'])
EDXNOTES_PUBLIC_API = 'http://{}:8042/api/v1'.format(os.environ['BOK_CHOY_HOSTNAME'])

View File

@@ -2519,7 +2519,7 @@ if FEATURES.get('ENABLE_CORS_HEADERS'):
# to simulate cross-domain requests.
XDOMAIN_PROXY_CACHE_TIMEOUT = 60 * 15
LOGIN_REDIRECT_WHITELIST = []
LOGIN_REDIRECT_WHITELIST = [CMS_BASE]
###################### Registration ##################################

View File

@@ -21,7 +21,7 @@ SITE_NAME = 'localhost:8000'
CELERY_ALWAYS_EAGER = True
HTTPS = 'off'
LMS_ROOT_URL = 'http://localhost:8000'
LMS_ROOT_URL = "http://localhost:8000"
LMS_INTERNAL_ROOT_URL = LMS_ROOT_URL
ENTERPRISE_API_URL = LMS_INTERNAL_ROOT_URL + '/enterprise/api/v1/'
@@ -229,7 +229,7 @@ CORS_ALLOW_HEADERS = corsheaders_default_headers + (
'use-jwt-cookie',
)
LOGIN_REDIRECT_WHITELIST = []
LOGIN_REDIRECT_WHITELIST = [CMS_BASE]
###################### JWTs ######################
JWT_AUTH.update({

View File

@@ -9,8 +9,8 @@ LOGGING['handlers']['local'] = LOGGING['handlers']['tracking'] = {
LOGGING['loggers']['tracking']['handlers'] = ['console']
LMS_BASE = 'edx.devstack.lms:18000'
CMS_BASE = 'edx.devstack.studio:18010'
LMS_BASE = 'localhost:18000'
CMS_BASE = 'localhost:18010'
SITE_NAME = LMS_BASE
LMS_ROOT_URL = 'http://{}'.format(LMS_BASE)
LMS_INTERNAL_ROOT_URL = LMS_ROOT_URL

View File

@@ -3,6 +3,7 @@ Django template context processors.
"""
from django.conf import settings
from django.utils.http import urlquote_plus
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
@@ -12,5 +13,7 @@ def configuration_context(request): # pylint: disable=unused-argument
Configuration context for django templates.
"""
return {
'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME)
'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME),
'current_url': urlquote_plus(request.build_absolute_uri(request.path)),
'current_site_url': urlquote_plus(request.build_absolute_uri('/')),
}

View File

@@ -46,7 +46,7 @@ class TestThemingViews(TestCase):
self.assertRedirects(
response,
'{login_url}?next={url}'.format(
login_url=settings.LOGIN_REDIRECT_URL,
login_url=settings.LOGIN_URL,
url=THEMING_ADMIN_URL,
)
)

View File

@@ -1,6 +1,8 @@
""" Test User Authentication utilities """
from collections import namedtuple
import ddt
from django.test import TestCase
from django.test.client import RequestFactory
from django.test.utils import override_settings
@@ -18,19 +20,27 @@ class TestRedirectUtils(TestCase):
super(TestRedirectUtils, self).setUp()
self.request = RequestFactory()
RedirectCase = namedtuple('RedirectCase', ['url', 'host', 'req_is_secure', 'expected_is_safe'])
@ddt.data(
('/dashboard', 'testserver', True),
('https://edx.org/courses', 'edx.org', True),
('https://test.edx.org/courses', 'edx.org', True),
('https://www.amazon.org', 'edx.org', False),
('http://edx.org/courses', 'edx.org', False),
('http:///edx.org/courses', 'edx.org', False), # Django's is_safe_url protects against "///"
RedirectCase('/dashboard', 'testserver', req_is_secure=True, expected_is_safe=True),
RedirectCase('https://test.edx.org/courses', 'edx.org', req_is_secure=True, expected_is_safe=True),
RedirectCase('https://www.amazon.org', 'edx.org', req_is_secure=True, expected_is_safe=False),
# https is required only if the request is_secure
RedirectCase('https://edx.org/courses', 'edx.org', req_is_secure=True, expected_is_safe=True),
RedirectCase('http://edx.org/courses', 'edx.org', req_is_secure=False, expected_is_safe=True),
RedirectCase('http://edx.org/courses', 'edx.org', req_is_secure=True, expected_is_safe=False),
# Django's is_safe_url protects against "///"
RedirectCase('http:///edx.org/courses', 'edx.org', req_is_secure=True, expected_is_safe=False),
)
@ddt.unpack
@override_settings(LOGIN_REDIRECT_WHITELIST=['test.edx.org'])
def test_safe_redirect(self, url, host, expected_is_safe):
def test_safe_redirect(self, url, host, req_is_secure, expected_is_safe):
""" Test safe next parameter """
req = self.request.get('/login', HTTP_HOST=host)
req.is_secure = lambda: req_is_secure
actual_is_safe = is_safe_login_or_logout_redirect(req, url)
self.assertEqual(actual_is_safe, expected_is_safe)

View File

@@ -23,5 +23,7 @@ def is_safe_login_or_logout_redirect(request, redirect_to):
if redirect_to in application.redirect_uris:
login_redirect_whitelist.add(urlparse(redirect_to).netloc)
is_safe_url = http.is_safe_url(redirect_to, allowed_hosts=login_redirect_whitelist, require_https=True)
is_safe_url = http.is_safe_url(
redirect_to, allowed_hosts=login_redirect_whitelist, require_https=request.is_secure(),
)
return is_safe_url

View File

@@ -29,11 +29,11 @@ class LogoutView(TemplateView):
@property
def target(self):
"""
If a redirect_url is specified in the querystring for this request, and the value is a url
with the same host, the view will redirect to this page after rendering the template.
If a redirect_url is specified in the querystring for this request, and the value is a safe
url for redirect, the view will redirect to this page after rendering the template.
If it is not specified, we will use the default target url.
"""
target_url = self.request.GET.get('redirect_url')
target_url = self.request.GET.get('redirect_url') or self.request.GET.get('next')
if target_url and is_safe_login_or_logout_redirect(self.request, target_url):
return target_url

View File

@@ -87,7 +87,7 @@ INSTALLED_APPS = (
'completion',
)
LMS_ROOT_URL = 'http://localhost:8000'
LMS_ROOT_URL = "http://localhost:8000"
MEDIA_ROOT = tempfile.mkdtemp()