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
a97a9500
Commit
a97a9500
authored
Nov 24, 2014
by
Bertrand Marron
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add IONISx auth platform
parent
a2a4e794
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
212 additions
and
12 deletions
+212
-12
cms/envs/aws.py
+22
-0
cms/envs/common.py
+8
-0
cms/startup.py
+14
-0
cms/urls.py
+9
-2
common/djangoapps/external_auth/login_and_register.py
+24
-2
common/djangoapps/student/views.py
+1
-6
common/djangoapps/third_party_auth/middleware.py
+58
-0
common/djangoapps/third_party_auth/pipeline.py
+20
-1
common/djangoapps/third_party_auth/portal.py
+47
-0
common/djangoapps/third_party_auth/settings.py
+5
-1
lms/envs/aws.py
+4
-0
No files found.
cms/envs/aws.py
View file @
a97a9500
...
...
@@ -11,6 +11,7 @@ This is the default template for our main set of AWS servers.
# and throws spurious errors. Therefore, we disable invalid-name checking.
# pylint: disable=invalid-name
import
datetime
import
json
from
.common
import
*
...
...
@@ -318,6 +319,27 @@ SESSION_INACTIVITY_TIMEOUT_IN_SECONDS = AUTH_TOKENS.get("SESSION_INACTIVITY_TIME
##### X-Frame-Options response header settings #####
X_FRAME_OPTIONS
=
ENV_TOKENS
.
get
(
'X_FRAME_OPTIONS'
,
X_FRAME_OPTIONS
)
##### Third-party auth options ################################################
if
FEATURES
.
get
(
'ENABLE_THIRD_PARTY_AUTH'
):
AUTHENTICATION_BACKENDS
=
(
ENV_TOKENS
.
get
(
'THIRD_PARTY_AUTH_BACKENDS'
,
[
'social.backends.google.GoogleOAuth2'
,
'social.backends.linkedin.LinkedinOAuth2'
,
'social.backends.facebook.FacebookOAuth2'
,
'third_party_auth.saml.SAMLAuthBackend'
])
+
list
(
AUTHENTICATION_BACKENDS
)
)
MIDDLEWARE_CLASSES
+=
(
'third_party_auth.middleware.PortalSynchronizerMiddleware'
,)
# The reduced session expiry time during the third party login pipeline. (Value in seconds)
SOCIAL_AUTH_PIPELINE_TIMEOUT
=
ENV_TOKENS
.
get
(
'SOCIAL_AUTH_PIPELINE_TIMEOUT'
,
600
)
# third_party_auth config moved to ConfigurationModels. This is for data migration only:
THIRD_PARTY_AUTH_OLD_CONFIG
=
AUTH_TOKENS
.
get
(
'THIRD_PARTY_AUTH'
,
None
)
IONISX_AUTH
=
AUTH_TOKENS
.
get
(
'IONISX_AUTH'
)
##### ADVANCED_SECURITY_CONFIG #####
ADVANCED_SECURITY_CONFIG
=
ENV_TOKENS
.
get
(
'ADVANCED_SECURITY_CONFIG'
,
{})
...
...
cms/envs/common.py
View file @
a97a9500
...
...
@@ -110,6 +110,10 @@ FEATURES = {
# Turn on/off Microsites feature
'USE_MICROSITES'
:
False
,
# Turn on third-party auth. Disabled for now because full implementations are not yet available. Remember to syncdb
# if you enable this; we don't create tables by default.
'ENABLE_THIRD_PARTY_AUTH'
:
False
,
# Allow creating courses with non-ascii characters in the course id
'ALLOW_UNICODE_COURSE_ID'
:
False
,
...
...
@@ -891,6 +895,10 @@ for app_name in OPTIONAL_APPS:
continue
INSTALLED_APPS
+=
(
app_name
,)
# Stub for third_party_auth options.
# See common/djangoapps/third_party_auth/settings.py for configuration details.
THIRD_PARTY_AUTH
=
{}
### ADVANCED_SECURITY_CONFIG
# Empty by default
ADVANCED_SECURITY_CONFIG
=
{}
...
...
cms/startup.py
View file @
a97a9500
...
...
@@ -24,6 +24,9 @@ def run():
if
settings
.
FEATURES
.
get
(
'USE_CUSTOM_THEME'
,
False
):
enable_theme
()
if
settings
.
FEATURES
.
get
(
'ENABLE_THIRD_PARTY_AUTH'
,
False
):
enable_third_party_auth
()
def
add_mimetypes
():
"""
...
...
@@ -68,3 +71,14 @@ def enable_theme():
settings
.
STATICFILES_DIRS
.
append
(
(
u'themes/{}'
.
format
(
settings
.
THEME_NAME
),
theme_root
/
'static'
)
)
def
enable_third_party_auth
():
"""
Enable the use of third_party_auth, which allows users to sign in to edX
using other identity providers. For configuration details, see
common/djangoapps/third_party_auth/settings.py.
"""
from
third_party_auth
import
settings
as
auth_settings
auth_settings
.
apply_settings
(
settings
)
cms/urls.py
View file @
a97a9500
...
...
@@ -58,6 +58,9 @@ urlpatterns += patterns(
url
(
r'^create_account$'
,
'student.views.create_account'
,
name
=
'create_account'
),
url
(
r'^activate/(?P<key>[^/]*)$'
,
'student.views.activate_account'
,
name
=
'activate'
),
url
(
r'^signin$'
,
'student.views.signin_user'
,
name
=
"login"
),
url
(
r'^signup$'
,
'student.views.signin_user'
,
name
=
'signup'
),
# ajax view that actually does the work
url
(
r'^login_post$'
,
'student.views.login_user'
,
name
=
'login_post'
),
...
...
@@ -70,8 +73,6 @@ urlpatterns += patterns(
url
(
r'^$'
,
'howitworks'
,
name
=
'homepage'
),
url
(
r'^howitworks$'
,
'howitworks'
),
url
(
r'^signup$'
,
'signup'
,
name
=
'signup'
),
url
(
r'^signin$'
,
'login_page'
,
name
=
'login'
),
url
(
r'^request_course_creator$'
,
'request_course_creator'
),
url
(
r'^course_team/{}(?:/(?P<email>.+))?$'
.
format
(
COURSELIKE_KEY_PATTERN
),
'course_team_handler'
),
...
...
@@ -183,6 +184,12 @@ if settings.FEATURES.get('CERTIFICATES_HTML_VIEW'):
'contentstore.views.certificates.certificates_list_handler'
)
)
# Third-party auth.
if
settings
.
FEATURES
.
get
(
'ENABLE_THIRD_PARTY_AUTH'
):
urlpatterns
+=
(
url
(
r''
,
include
(
'third_party_auth.urls'
)),
)
if
settings
.
DEBUG
:
try
:
from
.urls_dev
import
urlpatterns
as
dev_urlpatterns
...
...
common/djangoapps/external_auth/login_and_register.py
View file @
a97a9500
...
...
@@ -56,7 +56,18 @@ def login(request):
# is not handling the request.
response
=
None
if
settings
.
FEATURES
[
'AUTH_USE_CERTIFICATES'
]
and
external_auth
.
views
.
ssl_get_cert_from_request
(
request
):
if
settings
.
FEATURES
.
get
(
'ENABLE_THIRD_PARTY_AUTH'
):
# Redirect to IONISx, we don't want the registration form.
get
=
request
.
GET
.
copy
()
if
'course_id'
in
request
.
GET
:
request
.
session
[
'enroll_course_id'
]
=
request
.
GET
.
get
(
'course_id'
)
get
.
update
({
'next'
:
reverse
(
'about_course'
,
kwargs
=
{
'course_id'
:
unicode
(
request
.
GET
.
get
(
'course_id'
))})})
redirect_uri
=
reverse
(
'social:begin'
,
args
=
(
'portal-oauth2'
,))
response
=
external_auth
.
views
.
redirect_with_get
(
redirect_uri
,
get
,
do_reverse
=
False
)
elif
settings
.
FEATURES
[
'AUTH_USE_CERTIFICATES'
]
and
external_auth
.
views
.
ssl_get_cert_from_request
(
request
):
# SSL login doesn't require a view, so redirect
# branding and allow that to process the login if it
# is enabled and the header is in the request.
...
...
@@ -85,7 +96,18 @@ def register(request):
"""
response
=
None
if
settings
.
FEATURES
.
get
(
'AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'
):
if
settings
.
FEATURES
.
get
(
'ENABLE_THIRD_PARTY_AUTH'
):
# Redirect to IONISx, we don't want the registration form.
get
=
request
.
GET
.
copy
()
if
'course_id'
in
request
.
GET
:
request
.
session
[
'enroll_course_id'
]
=
request
.
GET
.
get
(
'course_id'
)
get
.
update
({
'next'
:
reverse
(
'about_course'
,
kwargs
=
{
'course_id'
:
unicode
(
request
.
GET
.
get
(
'course_id'
))})})
redirect_uri
=
reverse
(
'social:begin'
,
args
=
(
'portal-oauth2'
,))
response
=
external_auth
.
views
.
redirect_with_get
(
redirect_uri
,
get
,
do_reverse
=
False
)
elif
settings
.
FEATURES
.
get
(
'AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'
):
# Redirect to branding to process their certificate if SSL is enabled
# and registration is disabled.
response
=
external_auth
.
views
.
redirect_with_get
(
'root'
,
request
.
GET
)
...
...
common/djangoapps/student/views.py
View file @
a97a9500
...
...
@@ -1254,12 +1254,7 @@ def logout_user(request):
# We do not log here, because we have a handler registered
# to perform logging on successful logouts.
logout
(
request
)
if
settings
.
FEATURES
.
get
(
'AUTH_USE_CAS'
):
target
=
reverse
(
'cas-logout'
)
else
:
target
=
'/'
response
=
redirect
(
target
)
response
=
redirect
(
settings
.
IONISX_AUTH
.
get
(
'LOGOUT_URL'
))
delete_logged_in_cookies
(
response
)
return
response
...
...
common/djangoapps/third_party_auth/middleware.py
View file @
a97a9500
"""Middleware classes for third_party_auth."""
import
logging
import
requests
from
django.conf
import
settings
from
django.contrib.auth
import
logout
from
django.shortcuts
import
redirect
from
django.contrib.auth.models
import
AnonymousUser
from
student.models
import
UserProfile
from
social.apps.django_app.default
import
models
from
social.apps.django_app.middleware
import
SocialAuthExceptionMiddleware
from
.
import
pipeline
from
.
import
portal
log
=
logging
.
getLogger
(
__file__
)
class
ExceptionMiddleware
(
SocialAuthExceptionMiddleware
):
"""Custom middleware that handles conditional redirection."""
...
...
@@ -23,3 +35,49 @@ class ExceptionMiddleware(SocialAuthExceptionMiddleware):
redirect_uri
=
pipeline
.
AUTH_DISPATCH_URLS
[
auth_entry
]
return
redirect_uri
class
PortalSynchronizerMiddleware
(
object
):
"""Custom middleware to synchronize user status of LMS with Portal provider."""
def
process_request
(
self
,
request
):
if
request
.
user
.
is_authenticated
():
user
=
request
.
user
social_auth
=
models
.
DjangoStorage
.
user
.
get_social_auth_for_user
(
user
)
if
len
(
social_auth
)
==
1
:
social_data
=
social_auth
[
0
]
try
:
r
=
requests
.
get
(
settings
.
IONISX_AUTH
.
get
(
'USER_DATA_URL'
),
headers
=
{
'Authorization'
:
'Bearer {0}'
.
format
(
social_data
.
extra_data
[
'access_token'
])}
)
except
requests
.
ConnectionError
as
err
:
log
.
warning
(
err
)
return
body
=
r
.
json
()
if
r
.
status_code
!=
200
:
if
body
and
u'error'
in
body
and
u'redirectTo'
in
body
[
u'error'
]:
return
redirect
(
body
[
u'error'
][
u'redirectTo'
])
else
:
return
logout
(
request
)
if
body
:
_id
=
body
[
'_id'
]
email
=
portal
.
get_primary_email
(
body
[
'emails'
])
username
=
body
[
'username'
]
name
=
body
[
'name'
]
if
(
user
.
email
!=
email
or
user
.
username
!=
body
[
'username'
]):
log
.
info
(
'User {} needs to be updated'
.
format
(
_id
))
user
.
email
=
email
user
.
username
=
username
user
.
save
()
if
user
.
profile
.
name
!=
body
[
'name'
]:
log
.
info
(
'User profile for {} needs to be updated'
.
format
(
_id
))
user
.
profile
.
name
=
name
user
.
profile
.
save
()
common/djangoapps/third_party_auth/pipeline.py
View file @
a97a9500
...
...
@@ -62,6 +62,7 @@ import string # pylint: disable-msg=deprecated-module
from
collections
import
OrderedDict
import
urllib
import
analytics
import
logging
from
eventtracking
import
tracker
from
django.contrib.auth.models
import
User
...
...
@@ -73,6 +74,9 @@ from social.apps.django_app.default import models
from
social.exceptions
import
AuthException
from
social.pipeline
import
partial
from
social.pipeline.social_auth
import
associate_by_email
from
student.models
import
(
Registration
,
UserProfile
,
create_comments_service_user
)
import
student
...
...
@@ -83,6 +87,7 @@ from . import provider
# Note that this lives in openedx, so this dependency should be refactored.
from
openedx.core.djangoapps.user_api.preferences.api
import
update_email_opt_in
log
=
logging
.
getLogger
(
__file__
)
# These are the query string params you can pass
# to the URL that starts the authentication process.
...
...
@@ -441,7 +446,7 @@ def parse_query_params(strategy, response, *args, **kwargs):
"""Reads whitelisted query params, transforms them into pipeline args."""
auth_entry
=
strategy
.
session
.
get
(
AUTH_ENTRY_KEY
)
if
not
(
auth_entry
and
auth_entry
in
_AUTH_ENTRY_CHOICES
):
raise
AuthEntryError
(
strategy
.
request
.
backend
,
'auth_entry missing or invalid'
)
auth_entry
=
AUTH_ENTRY_LOGIN
return
{
'auth_entry'
:
auth_entry
}
...
...
@@ -476,6 +481,20 @@ def set_pipeline_timeout(strategy, user, *args, **kwargs):
# choice of the user.
def
create_user_from_oauth
(
strategy
,
details
,
user
,
is_new
,
*
args
,
**
kwargs
):
if
is_new
:
profile
=
UserProfile
(
user
=
user
)
profile
.
name
=
details
.
get
(
'fullname'
)
try
:
profile
.
save
()
except
Exception
:
log
.
error
(
"UserProfile creation failed for user {id}."
.
format
(
id
=
user
.
id
))
raise
create_comments_service_user
(
user
)
@partial.partial
def
ensure_user_information
(
strategy
,
auth_entry
,
backend
=
None
,
user
=
None
,
social
=
None
,
allow_inactive_user
=
False
,
*
args
,
**
kwargs
):
...
...
common/djangoapps/third_party_auth/portal.py
0 → 100644
View file @
a97a9500
from
requests
import
HTTPError
from
django.conf
import
settings
from
social.backends.oauth
import
BaseOAuth2
from
social.exceptions
import
AuthCanceled
def
get_primary_email
(
emails
):
for
email
in
emails
:
if
email
[
'primary'
]
is
True
:
return
email
[
'email'
]
return
None
class
PortalOAuth2
(
BaseOAuth2
):
"""Portal OAuth2 authentication backend"""
auth_settings
=
settings
.
IONISX_AUTH
name
=
'portal-oauth2'
ID_KEY
=
'_id'
AUTHORIZATION_URL
=
auth_settings
.
get
(
'AUTHORIZATION_URL'
)
ACCESS_TOKEN_URL
=
auth_settings
.
get
(
'ACCESS_TOKEN_URL'
)
ACCESS_TOKEN_METHOD
=
'POST'
REDIRECT_STATE
=
False
USER_DATA_URL
=
auth_settings
.
get
(
'USER_DATA_URL'
)
def
get_user_details
(
self
,
response
):
"""Return user details from IONISx account"""
return
{
'username'
:
response
[
'username'
],
'email'
:
get_primary_email
(
response
[
'emails'
]),
'fullname'
:
response
[
'name'
]
}
def
user_data
(
self
,
access_token
,
*
args
,
**
kwargs
):
"""Loads user data from service"""
return
self
.
get_json
(
self
.
USER_DATA_URL
,
headers
=
{
'Authorization'
:
'Bearer {0}'
.
format
(
access_token
)}
)
def
process_error
(
self
,
data
):
super
(
PortalOAuth2
,
self
)
.
process_error
(
data
)
if
data
.
get
(
'error_code'
):
raise
AuthCanceled
(
self
,
data
.
get
(
'error_message'
)
or
data
.
get
(
'error_code'
))
common/djangoapps/third_party_auth/settings.py
View file @
a97a9500
...
...
@@ -52,8 +52,12 @@ def apply_settings(django_settings):
'third_party_auth.pipeline.associate_by_email_if_login_api'
,
'social.pipeline.user.get_username'
,
'third_party_auth.pipeline.set_pipeline_timeout'
,
'third_party_auth.pipeline.ensure_user_information'
,
# IONISx: Disable as we always have enough information. :)
# 'third_party_auth.pipeline.ensure_user_information',
'social.pipeline.user.create_user'
,
'third_party_auth.pipeline.create_user_from_oauth'
,
'social.pipeline.social_auth.associate_user'
,
'social.pipeline.social_auth.load_extra_data'
,
'social.pipeline.user.user_details'
,
...
...
lms/envs/aws.py
View file @
a97a9500
...
...
@@ -548,6 +548,8 @@ if FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
])
+
list
(
AUTHENTICATION_BACKENDS
)
)
MIDDLEWARE_CLASSES
+=
(
'third_party_auth.middleware.PortalSynchronizerMiddleware'
,)
# The reduced session expiry time during the third party login pipeline. (Value in seconds)
SOCIAL_AUTH_PIPELINE_TIMEOUT
=
ENV_TOKENS
.
get
(
'SOCIAL_AUTH_PIPELINE_TIMEOUT'
,
600
)
...
...
@@ -560,6 +562,8 @@ if FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
'schedule'
:
datetime
.
timedelta
(
hours
=
ENV_TOKENS
.
get
(
'THIRD_PARTY_AUTH_SAML_FETCH_PERIOD_HOURS'
,
24
)),
}
IONISX_AUTH
=
AUTH_TOKENS
.
get
(
'IONISX_AUTH'
)
##### OAUTH2 Provider ##############
if
FEATURES
.
get
(
'ENABLE_OAUTH2_PROVIDER'
):
OAUTH_OIDC_ISSUER
=
ENV_TOKENS
[
'OAUTH_OIDC_ISSUER'
]
...
...
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