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 @@
...
@@ -2,6 +2,7 @@
import
logging
import
logging
import
mimetypes
import
mimetypes
import
urllib
import
urllib
import
urlparse
from
datetime
import
datetime
from
datetime
import
datetime
from
django.conf
import
settings
from
django.conf
import
settings
...
@@ -16,6 +17,7 @@ from pytz import UTC
...
@@ -16,6 +17,7 @@ from pytz import UTC
import
third_party_auth
import
third_party_auth
from
course_modes.models
import
CourseMode
from
course_modes.models
import
CourseMode
from
lms.djangoapps.verify_student.models
import
SoftwareSecurePhotoVerification
,
VerificationDeadline
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
from
openedx.core.djangoapps.theming.helpers
import
get_themes
# Enumeration of per-course verification statuses
# Enumeration of per-course verification statuses
...
@@ -240,6 +242,8 @@ def get_next_url_for_login_page(request):
...
@@ -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
Otherwise, we go to the ?next= query param or to the dashboard if nothing else is
specified.
specified.
If THIRD_PARTY_AUTH_HINT is set, then `tpa_hint=<hint>` is added as a query parameter.
"""
"""
redirect_to
=
get_redirect_to
(
request
)
redirect_to
=
get_redirect_to
(
request
)
if
not
redirect_to
:
if
not
redirect_to
:
...
@@ -247,6 +251,7 @@ def get_next_url_for_login_page(request):
...
@@ -247,6 +251,7 @@ def get_next_url_for_login_page(request):
redirect_to
=
reverse
(
'dashboard'
)
redirect_to
=
reverse
(
'dashboard'
)
except
NoReverseMatch
:
except
NoReverseMatch
:
redirect_to
=
reverse
(
'home'
)
redirect_to
=
reverse
(
'home'
)
if
any
(
param
in
request
.
GET
for
param
in
POST_AUTH_PARAMS
):
if
any
(
param
in
request
.
GET
for
param
in
POST_AUTH_PARAMS
):
# Before we redirect to next/dashboard, we need to handle auto-enrollment:
# 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
]
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):
...
@@ -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
# 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
# be saved in the session as part of the pipeline state. That URL will take priority
# over this one.
# 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
return
redirect_to
...
...
common/djangoapps/student/tests/test_helpers.py
View file @
98ee25fb
...
@@ -4,12 +4,16 @@ import logging
...
@@ -4,12 +4,16 @@ import logging
import
ddt
import
ddt
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.sessions.middleware
import
SessionMiddleware
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.test.client
import
RequestFactory
from
django.test.client
import
RequestFactory
from
django.test.utils
import
override_settings
from
mock
import
patch
from
testfixtures
import
LogCapture
from
testfixtures
import
LogCapture
from
student.helpers
import
get_next_url_for_login_page
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"
LOGGER_NAME
=
"student.helpers"
...
@@ -23,6 +27,13 @@ class TestLoginHelper(TestCase):
...
@@ -23,6 +27,13 @@ class TestLoginHelper(TestCase):
super
(
TestLoginHelper
,
self
)
.
setUp
()
super
(
TestLoginHelper
,
self
)
.
setUp
()
self
.
request
=
RequestFactory
()
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
(
@ddt.data
(
(
"https://www.amazon.com"
,
"text/html"
,
None
,
(
"https://www.amazon.com"
,
"text/html"
,
None
,
"Unsafe redirect parameter detected after login page: u'https://www.amazon.com'"
),
"Unsafe redirect parameter detected after login page: u'https://www.amazon.com'"
),
...
@@ -56,3 +67,38 @@ class TestLoginHelper(TestCase):
...
@@ -56,3 +67,38 @@ class TestLoginHelper(TestCase):
req
.
META
[
"HTTP_ACCEPT"
]
=
"text/html"
# pylint: disable=no-member
req
.
META
[
"HTTP_ACCEPT"
]
=
"text/html"
# pylint: disable=no-member
next_page
=
get_next_url_for_login_page
(
req
)
next_page
=
get_next_url_for_login_page
(
req
)
self
.
assertEqual
(
next_page
,
u'/dashboard'
)
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
...
@@ -458,15 +458,63 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
response
=
self
.
client
.
get
(
reverse
(
'signin_user'
),
params
,
HTTP_ACCEPT
=
"text/html"
)
response
=
self
.
client
.
get
(
reverse
(
'signin_user'
),
params
,
HTTP_ACCEPT
=
"text/html"
)
self
.
assertNotIn
(
response
.
content
,
tpa_hint
)
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. """
"""Test that the dialog doesn't show up for hinted logins when disabled. """
self
.
google_provider
.
skip_hinted_login_dialog
=
True
self
.
google_provider
.
skip_hinted_login_dialog
=
True
self
.
google_provider
.
save
()
self
.
google_provider
.
save
()
params
=
[(
"next"
,
"/courses/something/?tpa_hint=oa2-google-oauth2"
)]
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
(
self
.
assertRedirects
(
response
,
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
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"):
...
@@ -86,13 +86,17 @@ def login_and_registration_form(request, initial_mode="login"):
if
tpa_hint_provider
.
skip_hinted_login_dialog
:
if
tpa_hint_provider
.
skip_hinted_login_dialog
:
# Forward the user directly to the provider's login URL when the provider is configured
# Forward the user directly to the provider's login URL when the provider is configured
# to skip the dialog.
# to skip the dialog.
if
initial_mode
==
"register"
:
auth_entry
=
pipeline
.
AUTH_ENTRY_REGISTER
else
:
auth_entry
=
pipeline
.
AUTH_ENTRY_LOGIN
return
redirect
(
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
third_party_auth_hint
=
provider_id
initial_mode
=
"hinted_login"
initial_mode
=
"hinted_login"
except
(
KeyError
,
ValueError
,
IndexError
):
except
(
KeyError
,
ValueError
,
IndexError
)
as
ex
:
pass
log
.
error
(
"Unknown tpa_hint provider:
%
s"
,
ex
)
# If this is a themed site, revert to the old login/registration pages.
# If this is a themed site, revert to the old login/registration pages.
# We need to do this for now to support existing themes.
# 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