diff --git a/cms/djangoapps/contentstore/features/signup.feature b/cms/djangoapps/contentstore/features/signup.feature
index 9706cfe7ea3a..01c912deca44 100644
--- a/cms/djangoapps/contentstore/features/signup.feature
+++ b/cms/djangoapps/contentstore/features/signup.feature
@@ -10,3 +10,20 @@ Feature: Sign in
And I press the Create My Account button on the registration form
Then I should see be on the studio home page
And I should see the message "complete your sign up we need you to verify your email address"
+
+ Scenario: Login with a valid redirect
+ Given I have opened a new course in Studio
+ And I am not logged in
+ And I visit the url "/MITx/999/course/Robot_Super_Course"
+ And I should see that the path is "/signin?next=/MITx/999/course/Robot_Super_Course"
+ When I fill in and submit the signin form
+ And I wait for "2" seconds
+ Then I should see that the path is "/MITx/999/course/Robot_Super_Course"
+
+ Scenario: Login with an invalid redirect
+ Given I have opened a new course in Studio
+ And I am not logged in
+ And I visit the url "/signin?next=http://www.google.com/"
+ When I fill in and submit the signin form
+ And I wait for "2" seconds
+ Then I should see that the path is "/"
diff --git a/cms/djangoapps/contentstore/features/signup.py b/cms/djangoapps/contentstore/features/signup.py
index 8957666b0601..e9abb55a78ed 100644
--- a/cms/djangoapps/contentstore/features/signup.py
+++ b/cms/djangoapps/contentstore/features/signup.py
@@ -30,3 +30,13 @@ def i_should_see_be_on_the_studio_home_page(step):
@step(u'I should see the message "([^"]*)"$')
def i_should_see_the_message(step, msg):
assert world.browser.is_text_present(msg, 5)
+
+
+@step(u'I fill in and submit the signin form$')
+def i_fill_in_the_signin_form(step):
+ def fill_login_form():
+ login_form = world.browser.find_by_css('form#login_form')
+ login_form.find_by_name('email').fill('robot+studio@edx.org')
+ login_form.find_by_name('password').fill('test')
+ login_form.find_by_name('submit').click()
+ world.retry_on_exception(fill_login_form)
diff --git a/cms/envs/common.py b/cms/envs/common.py
index 0ca0d1b65fbd..c2b9e6454f58 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -247,7 +247,7 @@
'js/models/section.js', 'js/views/section.js',
'js/models/metadata_model.js', 'js/views/metadata_editor_view.js',
'js/models/textbook.js', 'js/views/textbook.js',
- 'js/views/assets.js'],
+ 'js/views/assets.js', 'js/utility.js'],
'output_filename': 'js/cms-application.js',
'test_order': 0
},
diff --git a/cms/templates/login.html b/cms/templates/login.html
index 1997fd23fe66..3b41be1a7dd9 100644
--- a/cms/templates/login.html
+++ b/cms/templates/login.html
@@ -79,7 +79,7 @@
${_("Need Help?")}
function(json) {
if(json.success) {
var next = /next=([^&]*)/g.exec(decodeURIComponent(window.location.search));
- if (next && next.length > 1) {
+ if (next && next.length > 1 && !isExternal(next[1])) {
location.href = next[1];
}
else location.href = "${reverse('homepage')}";
diff --git a/common/djangoapps/terrain/course_helpers.py b/common/djangoapps/terrain/course_helpers.py
index c62b1a1e79af..081e2a3bc223 100644
--- a/common/djangoapps/terrain/course_helpers.py
+++ b/common/djangoapps/terrain/course_helpers.py
@@ -65,7 +65,7 @@ def log_in(username, password):
@world.absorb
def register_by_course_id(course_id, is_staff=False):
- create_user('robot')
+ create_user('robot', 'password')
u = User.objects.get(username='robot')
if is_staff:
u.is_staff = True
diff --git a/common/djangoapps/terrain/steps.py b/common/djangoapps/terrain/steps.py
index cfa8de15b7d6..1c2e1e624c08 100644
--- a/common/djangoapps/terrain/steps.py
+++ b/common/djangoapps/terrain/steps.py
@@ -168,6 +168,11 @@ def dialogs_are_closed(step):
assert world.dialogs_closed()
+@step(u'visit the url "([^"]*)"')
+def visit_url(step, url):
+ world.browser.visit(django_url(url))
+
+
@step('I will confirm all alerts')
def i_confirm_all_alerts(step):
"""
diff --git a/common/static/js/utility.js b/common/static/js/utility.js
new file mode 100644
index 000000000000..6407faad4807
--- /dev/null
+++ b/common/static/js/utility.js
@@ -0,0 +1,20 @@
+// checks whether or not the url is external to the local site.
+// generously provided by StackOverflow: http://stackoverflow.com/questions/6238351/fastest-way-to-detect-external-urls
+function isExternal(url) {
+ // parse the url into protocol, host, path, query, and fragment. More information can be found here: http://tools.ietf.org/html/rfc3986#appendix-B
+ var match = url.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);
+ // match[1] matches a protocol if one exists in the url
+ // if the protocol in the url does not match the protocol in the window's location, this url is considered external
+ if (typeof match[1] === "string" &&
+ match[1].length > 0
+ && match[1].toLowerCase() !== location.protocol)
+ return true;
+ // match[2] matches the host if one exists in the url
+ // if the host in the url does not match the host of the window location, this url is considered external
+ if (typeof match[2] === "string" &&
+ match[2].length > 0 &&
+ // this regex removes the port number if it patches the current location's protocol
+ match[2].replace(new RegExp(":("+{"http:":80,"https:":443}[location.protocol]+")?$"), "") !== location.host)
+ return true;
+ return false;
+}
diff --git a/lms/djangoapps/courseware/features/login.feature b/lms/djangoapps/courseware/features/login.feature
index a1b788a7b2aa..b22953317176 100644
--- a/lms/djangoapps/courseware/features/login.feature
+++ b/lms/djangoapps/courseware/features/login.feature
@@ -25,3 +25,21 @@ Feature: Login in as a registered user
And I click the link with the text "Log Out"
Then I should see a link with the text "Log in"
And I should see that the path is "/"
+
+ Scenario: Login with valid redirect
+ Given I am an edX user
+ And The course "6.002x" exists
+ And I am registered for the course "6.002x"
+ And I am not logged in
+ And I visit the url "/courses/edx/6.002x/Test_Course/courseware"
+ And I should see that the path is "/accounts/login?next=/courses/edx/6.002x/Test_Course/courseware"
+ When I submit my credentials on the login form
+ And I wait for "2" seconds
+ Then the page title should contain "6.002x Courseware"
+
+ Scenario: Login with an invalid redirect
+ Given I am an edX user
+ And I am not logged in
+ And I visit the url "/login?next=http://www.google.com/"
+ When I submit my credentials on the login form
+ Then I should be on the dashboard page
diff --git a/lms/envs/common.py b/lms/envs/common.py
index a0cc96c49d67..5a87ee9323e1 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -95,19 +95,19 @@
# This flag disables the requirement of having to agree to the TOS for users registering
# with Shib. Feature was requested by Stanford's office of general counsel
'SHIB_DISABLE_TOS': False,
-
+
# Enables ability to restrict enrollment in specific courses by the user account login method
'RESTRICT_ENROLL_BY_REG_METHOD': False,
# analytics experiments
'ENABLE_INSTRUCTOR_ANALYTICS': False,
- # enable analytics server.
+ # enable analytics server.
# WARNING: THIS SHOULD ALWAYS BE SET TO FALSE UNDER NORMAL
# LMS OPERATION. See analytics.py for details about what
- # this does.
+ # this does.
- 'RUN_AS_ANALYTICS_SERVER_ENABLED' : False,
+ 'RUN_AS_ANALYTICS_SERVER_ENABLED': False,
# Flip to True when the YouTube iframe API breaks (again)
'USE_YOUTUBE_OBJECT_API': False,
@@ -571,6 +571,7 @@
'js/toggle_login_modal.js',
'js/sticky_filter.js',
'js/query-params.js',
+ 'js/utility.js',
],
'output_filename': 'js/lms-application.js',
diff --git a/lms/templates/login.html b/lms/templates/login.html
index 5ab63a86c216..b7f92cc2a39c 100644
--- a/lms/templates/login.html
+++ b/lms/templates/login.html
@@ -30,7 +30,7 @@
$("label").parent().removeClass("is-focused");
});
});
-
+
(function() {
toggleSubmitButton(true);
@@ -46,7 +46,7 @@
if(json.success) {
var u=decodeURI(window.location.search);
next=u.split("next=")[1];
- if (next) {
+ if (next && !isExternal(next)) {
location.href=next;
} else {
location.href="${reverse('dashboard')}";