Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
98ee25fb
Commit
98ee25fb
authored
Aug 18, 2017
by
George Song
Committed by
GitHub
Aug 18, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15587 from open-craft/jill/tpa-hint-setting
Adds settings.FEATURES['THIRD_PARTY_AUTH_HINT']
parents
36e4c801
84c6c5ac
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
126 additions
and
6 deletions
+126
-6
common/djangoapps/student/helpers.py
+22
-0
common/djangoapps/student/tests/test_helpers.py
+46
-0
lms/djangoapps/student_account/test/test_views.py
+51
-3
lms/djangoapps/student_account/views.py
+7
-3
No files found.
common/djangoapps/student/helpers.py
View file @
98ee25fb
...
...
@@ -2,6 +2,7 @@
import
logging
import
mimetypes
import
urllib
import
urlparse
from
datetime
import
datetime
from
django.conf
import
settings
...
...
@@ -16,6 +17,7 @@ from pytz import UTC
import
third_party_auth
from
course_modes.models
import
CourseMode
from
lms.djangoapps.verify_student.models
import
SoftwareSecurePhotoVerification
,
VerificationDeadline
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.djangoapps.theming.helpers
import
get_themes
# Enumeration of per-course verification statuses
...
...
@@ -240,6 +242,8 @@ def get_next_url_for_login_page(request):
Otherwise, we go to the ?next= query param or to the dashboard if nothing else is
specified.
If THIRD_PARTY_AUTH_HINT is set, then `tpa_hint=<hint>` is added as a query parameter.
"""
redirect_to
=
get_redirect_to
(
request
)
if
not
redirect_to
:
...
...
@@ -247,6 +251,7 @@ def get_next_url_for_login_page(request):
redirect_to
=
reverse
(
'dashboard'
)
except
NoReverseMatch
:
redirect_to
=
reverse
(
'home'
)
if
any
(
param
in
request
.
GET
for
param
in
POST_AUTH_PARAMS
):
# Before we redirect to next/dashboard, we need to handle auto-enrollment:
params
=
[(
param
,
request
.
GET
[
param
])
for
param
in
POST_AUTH_PARAMS
if
param
in
request
.
GET
]
...
...
@@ -255,6 +260,23 @@ def get_next_url_for_login_page(request):
# Note: if we are resuming a third party auth pipeline, then the next URL will already
# be saved in the session as part of the pipeline state. That URL will take priority
# over this one.
# Append a tpa_hint query parameter, if one is configured
tpa_hint
=
configuration_helpers
.
get_value
(
"THIRD_PARTY_AUTH_HINT"
,
settings
.
FEATURES
.
get
(
"THIRD_PARTY_AUTH_HINT"
,
''
)
)
if
tpa_hint
:
# Don't add tpa_hint if we're already in the TPA pipeline (prevent infinite loop),
# and don't overwrite any existing tpa_hint params (allow tpa_hint override).
running_pipeline
=
third_party_auth
.
pipeline
.
get
(
request
)
(
scheme
,
netloc
,
path
,
query
,
fragment
)
=
list
(
urlparse
.
urlsplit
(
redirect_to
))
if
not
running_pipeline
and
'tpa_hint'
not
in
query
:
params
=
urlparse
.
parse_qs
(
query
)
params
[
'tpa_hint'
]
=
[
tpa_hint
]
query
=
urllib
.
urlencode
(
params
,
doseq
=
True
)
redirect_to
=
urlparse
.
urlunsplit
((
scheme
,
netloc
,
path
,
query
,
fragment
))
return
redirect_to
...
...
common/djangoapps/student/tests/test_helpers.py
View file @
98ee25fb
...
...
@@ -4,12 +4,16 @@ import logging
import
ddt
from
django.conf
import
settings
from
django.contrib.sessions.middleware
import
SessionMiddleware
from
django.core.urlresolvers
import
reverse
from
django.test
import
TestCase
from
django.test.client
import
RequestFactory
from
django.test.utils
import
override_settings
from
mock
import
patch
from
testfixtures
import
LogCapture
from
student.helpers
import
get_next_url_for_login_page
from
openedx.core.djangoapps.site_configuration.tests.test_util
import
with_site_configuration_context
LOGGER_NAME
=
"student.helpers"
...
...
@@ -23,6 +27,13 @@ class TestLoginHelper(TestCase):
super
(
TestLoginHelper
,
self
)
.
setUp
()
self
.
request
=
RequestFactory
()
@staticmethod
def
_add_session
(
request
):
"""Annotate the request object with a session"""
middleware
=
SessionMiddleware
()
middleware
.
process_request
(
request
)
request
.
session
.
save
()
@ddt.data
(
(
"https://www.amazon.com"
,
"text/html"
,
None
,
"Unsafe redirect parameter detected after login page: u'https://www.amazon.com'"
),
...
...
@@ -56,3 +67,38 @@ class TestLoginHelper(TestCase):
req
.
META
[
"HTTP_ACCEPT"
]
=
"text/html"
# pylint: disable=no-member
next_page
=
get_next_url_for_login_page
(
req
)
self
.
assertEqual
(
next_page
,
u'/dashboard'
)
@patch
(
'student.helpers.third_party_auth.pipeline.get'
)
@ddt.data
(
# Test requests outside the TPA pipeline - tpa_hint should be added.
(
None
,
'/dashboard'
,
'/dashboard'
,
False
),
(
''
,
'/dashboard'
,
'/dashboard'
,
False
),
(
''
,
'/dashboard?tpa_hint=oa2-google-oauth2'
,
'/dashboard?tpa_hint=oa2-google-oauth2'
,
False
),
(
'saml-idp'
,
'/dashboard'
,
'/dashboard?tpa_hint=saml-idp'
,
False
),
# THIRD_PARTY_AUTH_HINT can be overridden via the query string
(
'saml-idp'
,
'/dashboard?tpa_hint=oa2-google-oauth2'
,
'/dashboard?tpa_hint=oa2-google-oauth2'
,
False
),
# Test requests inside the TPA pipeline - tpa_hint should not be added, preventing infinite loop.
(
None
,
'/dashboard'
,
'/dashboard'
,
True
),
(
''
,
'/dashboard'
,
'/dashboard'
,
True
),
(
''
,
'/dashboard?tpa_hint=oa2-google-oauth2'
,
'/dashboard?tpa_hint=oa2-google-oauth2'
,
True
),
(
'saml-idp'
,
'/dashboard'
,
'/dashboard'
,
True
),
# OK to leave tpa_hint overrides in place.
(
'saml-idp'
,
'/dashboard?tpa_hint=oa2-google-oauth2'
,
'/dashboard?tpa_hint=oa2-google-oauth2'
,
True
),
)
@ddt.unpack
def
test_third_party_auth_hint
(
self
,
tpa_hint
,
next_url
,
expected_url
,
running_pipeline
,
mock_running_pipeline
):
mock_running_pipeline
.
return_value
=
running_pipeline
def
validate_login
():
req
=
self
.
request
.
get
(
reverse
(
"login"
)
+
"?next={url}"
.
format
(
url
=
next_url
))
req
.
META
[
"HTTP_ACCEPT"
]
=
"text/html"
# pylint: disable=no-member
self
.
_add_session
(
req
)
next_page
=
get_next_url_for_login_page
(
req
)
self
.
assertEqual
(
next_page
,
expected_url
)
with
override_settings
(
FEATURES
=
dict
(
settings
.
FEATURES
,
THIRD_PARTY_AUTH_HINT
=
tpa_hint
)):
validate_login
()
with
with_site_configuration_context
(
configuration
=
dict
(
THIRD_PARTY_AUTH_HINT
=
tpa_hint
)):
validate_login
()
lms/djangoapps/student_account/test/test_views.py
View file @
98ee25fb
...
...
@@ -458,15 +458,63 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
response
=
self
.
client
.
get
(
reverse
(
'signin_user'
),
params
,
HTTP_ACCEPT
=
"text/html"
)
self
.
assertNotIn
(
response
.
content
,
tpa_hint
)
def
test_hinted_login_dialog_disabled
(
self
):
@ddt.data
(
(
'signin_user'
,
'login'
),
(
'register_user'
,
'register'
),
)
@ddt.unpack
def
test_hinted_login_dialog_disabled
(
self
,
url_name
,
auth_entry
):
"""Test that the dialog doesn't show up for hinted logins when disabled. """
self
.
google_provider
.
skip_hinted_login_dialog
=
True
self
.
google_provider
.
save
()
params
=
[(
"next"
,
"/courses/something/?tpa_hint=oa2-google-oauth2"
)]
response
=
self
.
client
.
get
(
reverse
(
'signin_user'
),
params
,
HTTP_ACCEPT
=
"text/html"
)
response
=
self
.
client
.
get
(
reverse
(
url_name
),
params
,
HTTP_ACCEPT
=
"text/html"
)
self
.
assertRedirects
(
response
,
'auth/login/google-oauth2/?auth_entry={}&next=
%2
Fcourses
%2
Fsomething
%2
F
%3
Ftpa_hint
%3
Doa2-google-oauth2'
.
format
(
auth_entry
),
target_status_code
=
302
)
@override_settings
(
FEATURES
=
dict
(
settings
.
FEATURES
,
THIRD_PARTY_AUTH_HINT
=
'oa2-google-oauth2'
))
@ddt.data
(
'signin_user'
,
'register_user'
,
)
def
test_settings_tpa_hinted_login
(
self
,
url_name
):
"""
Ensure that settings.FEATURES['THIRD_PARTY_AUTH_HINT'] can set third_party_auth_hint.
"""
params
=
[(
"next"
,
"/courses/something/"
)]
response
=
self
.
client
.
get
(
reverse
(
url_name
),
params
,
HTTP_ACCEPT
=
"text/html"
)
self
.
assertContains
(
response
,
'"third_party_auth_hint": "oa2-google-oauth2"'
)
# THIRD_PARTY_AUTH_HINT can be overridden via the query string
tpa_hint
=
self
.
hidden_enabled_provider
.
provider_id
params
=
[(
"next"
,
"/courses/something/?tpa_hint={0}"
.
format
(
tpa_hint
))]
response
=
self
.
client
.
get
(
reverse
(
url_name
),
params
,
HTTP_ACCEPT
=
"text/html"
)
self
.
assertContains
(
response
,
'"third_party_auth_hint": "{0}"'
.
format
(
tpa_hint
))
# Even disabled providers in the query string will override THIRD_PARTY_AUTH_HINT
tpa_hint
=
self
.
hidden_disabled_provider
.
provider_id
params
=
[(
"next"
,
"/courses/something/?tpa_hint={0}"
.
format
(
tpa_hint
))]
response
=
self
.
client
.
get
(
reverse
(
url_name
),
params
,
HTTP_ACCEPT
=
"text/html"
)
self
.
assertNotIn
(
response
.
content
,
tpa_hint
)
@override_settings
(
FEATURES
=
dict
(
settings
.
FEATURES
,
THIRD_PARTY_AUTH_HINT
=
'oa2-google-oauth2'
))
@ddt.data
(
(
'signin_user'
,
'login'
),
(
'register_user'
,
'register'
),
)
@ddt.unpack
def
test_settings_tpa_hinted_login_dialog_disabled
(
self
,
url_name
,
auth_entry
):
"""Test that the dialog doesn't show up for hinted logins when disabled via settings.THIRD_PARTY_AUTH_HINT. """
self
.
google_provider
.
skip_hinted_login_dialog
=
True
self
.
google_provider
.
save
()
params
=
[(
"next"
,
"/courses/something/"
)]
response
=
self
.
client
.
get
(
reverse
(
url_name
),
params
,
HTTP_ACCEPT
=
"text/html"
)
self
.
assertRedirects
(
response
,
'auth/login/google-oauth2/?auth_entry=
login&next=
%2
Fcourses
%2
Fsomething
%2
F
%3
Ftpa_hint
%3
Doa2-google-oauth2'
,
'auth/login/google-oauth2/?auth_entry=
{}&next=
%2
Fcourses
%2
Fsomething
%2
F
%3
Ftpa_hint
%3
Doa2-google-oauth2'
.
format
(
auth_entry
)
,
target_status_code
=
302
)
...
...
lms/djangoapps/student_account/views.py
View file @
98ee25fb
...
...
@@ -86,13 +86,17 @@ def login_and_registration_form(request, initial_mode="login"):
if
tpa_hint_provider
.
skip_hinted_login_dialog
:
# Forward the user directly to the provider's login URL when the provider is configured
# to skip the dialog.
if
initial_mode
==
"register"
:
auth_entry
=
pipeline
.
AUTH_ENTRY_REGISTER
else
:
auth_entry
=
pipeline
.
AUTH_ENTRY_LOGIN
return
redirect
(
pipeline
.
get_login_url
(
provider_id
,
pipeline
.
AUTH_ENTRY_LOGIN
,
redirect_url
=
redirect_to
)
pipeline
.
get_login_url
(
provider_id
,
auth_entry
,
redirect_url
=
redirect_to
)
)
third_party_auth_hint
=
provider_id
initial_mode
=
"hinted_login"
except
(
KeyError
,
ValueError
,
IndexError
):
pass
except
(
KeyError
,
ValueError
,
IndexError
)
as
ex
:
log
.
error
(
"Unknown tpa_hint provider:
%
s"
,
ex
)
# If this is a themed site, revert to the old login/registration pages.
# We need to do this for now to support existing themes.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment