diff --git a/cms/djangoapps/contentstore/features/signup.feature b/cms/djangoapps/contentstore/features/signup.feature index 9706cfe7ea..01c912deca 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 8957666b06..e9abb55a78 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 f5baa3211b..bdb3ec6fde 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -247,7 +247,7 @@ PIPELINE_JS = { '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 1997fd23fe..3b41be1a7d 100644 --- a/cms/templates/login.html +++ b/cms/templates/login.html @@ -79,7 +79,7 @@ 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 c62b1a1e79..081e2a3bc2 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 cfa8de15b7..1c2e1e624c 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 0000000000..6407faad48 --- /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 a1b788a7b2..b229533171 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 a0cc96c49d..5a87ee9323 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -95,19 +95,19 @@ MITX_FEATURES = { # 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 @@ PIPELINE_JS = { '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 5ab63a86c2..b7f92cc2a3 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')}";