From 10ebcefd5427a8083581a877990517d0485ee5b1 Mon Sep 17 00:00:00 2001
From: ichuang
Date: Wed, 1 Aug 2012 14:59:55 -0400
Subject: [PATCH] add openid authentication
---
common/djangoapps/student/views.py | 45 +++++-
lms/envs/dev.py | 12 ++
lms/envs/dev_ike.py | 222 ++++++++++++-----------------
lms/templates/extauth_failure.html | 11 ++
lms/templates/index.html | 9 ++
lms/templates/login_modal.html | 3 +
lms/templates/signup_modal.html | 15 ++
lms/urls.py | 12 +-
requirements.txt | 2 +
9 files changed, 199 insertions(+), 132 deletions(-)
create mode 100644 lms/templates/extauth_failure.html
diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py
index b739e0e37c..692b135ff6 100644
--- a/common/djangoapps/student/views.py
+++ b/common/djangoapps/student/views.py
@@ -61,6 +61,15 @@ def index(request):
if settings.COURSEWARE_ENABLED and request.user.is_authenticated():
return redirect(reverse('dashboard'))
+ return main_index()
+
+def main_index(extra_context = {}):
+ '''
+ Render the edX main page.
+
+ extra_context is used to allow immediate display of certain modal windows, eg signup,
+ as used by external_auth.
+ '''
feed_data = cache.get("students_index_rss_feed_data")
if feed_data == None:
if hasattr(settings, 'RSS_URL'):
@@ -81,8 +90,9 @@ def index(request):
for course in courses:
universities[course.org].append(course)
- return render_to_response('index.html', {'universities': universities, 'entries': entries})
-
+ context = {'universities': universities, 'entries': entries}
+ context.update(extra_context)
+ return render_to_response('index.html', context)
def course_from_id(id):
course_loc = CourseDescriptor.id_to_location(id)
@@ -257,11 +267,26 @@ def change_setting(request):
@ensure_csrf_cookie
def create_account(request, post_override=None):
- ''' JSON call to enroll in the course. '''
+ '''
+ JSON call to create new edX account.
+ Used by form in signup_modal.html, which is included into navigation.html
+ '''
js = {'success': False}
post_vars = post_override if post_override else request.POST
+ # if doing signup for an external authorization, then get email, password, name from the eamap
+ # don't use the ones from the form, since the user could have hacked those
+ doExternalAuth = 'ExternalAuthMap' in request.session
+ if doExternalAuth:
+ eamap = request.session['ExternalAuthMap']
+ email = eamap.external_email
+ name = eamap.external_name
+ password = eamap.internal_password
+ post_vars = dict(post_vars.items())
+ post_vars.update(dict(email=email, name=name, password=password, username=post_vars['username']))
+ log.debug('extauth test: post_vars = %s' % post_vars)
+
# Confirm we have a properly formed request
for a in ['username', 'email', 'password', 'name']:
if a not in post_vars:
@@ -356,8 +381,9 @@ def create_account(request, post_override=None):
'key': r.activation_key,
}
+ # composes activation email
subject = render_to_string('emails/activation_email_subject.txt', d)
- # Email subject *must not* contain newlines
+ # Email subject *must not* contain newlines
subject = ''.join(subject.splitlines())
message = render_to_string('emails/activation_email.txt', d)
@@ -382,6 +408,17 @@ def create_account(request, post_override=None):
try_change_enrollment(request)
+ if doExternalAuth:
+ eamap.user = login_user
+ eamap.dtsignup = datetime.datetime.now()
+ eamap.save()
+ log.debug('Updated ExternalAuthMap for %s to be %s' % (post_vars['username'],eamap))
+
+ if settings.MITX_FEATURES.get('BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH'):
+ log.debug('bypassing activation email')
+ login_user.is_active = True
+ login_user.save()
+
js = {'success': True}
return HttpResponse(json.dumps(js), mimetype="application/json")
diff --git a/lms/envs/dev.py b/lms/envs/dev.py
index 1a2659cb1f..f9b7ba10a0 100644
--- a/lms/envs/dev.py
+++ b/lms/envs/dev.py
@@ -55,6 +55,18 @@ CACHES = {
# Dummy secret key for dev
SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
+################################ OpenID Auth #################################
+MITX_FEATURES['AUTH_USE_OPENID'] = True
+MITX_FEATURES['BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH'] = True
+
+INSTALLED_APPS += ('external_auth',)
+INSTALLED_APPS += ('django_openid_auth',)
+
+OPENID_CREATE_USERS = False
+OPENID_UPDATE_DETAILS_FROM_SREG = True
+OPENID_SSO_SERVER_URL = 'https://www.google.com/accounts/o8/id' # TODO: accept more endpoints
+OPENID_USE_AS_ADMIN_LOGIN = False
+
################################ DEBUG TOOLBAR #################################
INSTALLED_APPS += ('debug_toolbar',)
MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
diff --git a/lms/envs/dev_ike.py b/lms/envs/dev_ike.py
index 79ae3354ac..fb7d980550 100644
--- a/lms/envs/dev_ike.py
+++ b/lms/envs/dev_ike.py
@@ -7,142 +7,110 @@ sessions. Assumes structure:
/mitx # The location of this repo
/log # Where we're going to write log files
"""
-
-import socket
-
-if 'eecs1' in socket.gethostname():
- MITX_ROOT_URL = '/mitx2'
-
from .common import *
from .logsettings import get_logger_config
-from .dev import *
-
-if 'eecs1' in socket.gethostname():
- # MITX_ROOT_URL = '/mitx2'
- MITX_ROOT_URL = 'https://eecs1.mit.edu/mitx2'
-
-#-----------------------------------------------------------------------------
-# edx4edx content server
-
-EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
-MITX_FEATURES['REROUTE_ACTIVATION_EMAIL'] = 'ichuang@mit.edu'
-EDX4EDX_ROOT = ENV_ROOT / "data/edx4edx"
-
-#EMAIL_BACKEND = 'django_ses.SESBackend'
-
-#-----------------------------------------------------------------------------
-# ichuang
DEBUG = True
-ENABLE_MULTICOURSE = True # set to False to disable multicourse display (see lib.util.views.mitxhome)
-QUICKEDIT = False
+TEMPLATE_DEBUG = True
-MAKO_TEMPLATES['course'] = [DATA_DIR, EDX4EDX_ROOT ]
+MITX_FEATURES['DISABLE_START_DATES'] = True
-#MITX_FEATURES['USE_DJANGO_PIPELINE'] = False
-MITX_FEATURES['DISPLAY_HISTOGRAMS_TO_STAFF'] = False
-MITX_FEATURES['DISPLAY_EDIT_LINK'] = True
-MITX_FEATURES['DEBUG_LEVEL'] = 10 # 0 = lowest level, least verbose, 255 = max level, most verbose
+WIKI_ENABLED = True
-COURSE_SETTINGS = {'6.002x_Fall_2012': {'number' : '6.002x',
- 'title' : 'Circuits and Electronics',
- 'xmlpath': '/6002x-fall-2012/',
- 'active' : True,
- 'default_chapter' : 'Week_1',
- 'default_section' : 'Administrivia_and_Circuit_Elements',
- 'location': 'i4x://edx/6002xs12/course/6.002x_Fall_2012',
- },
- '8.02_Spring_2013': {'number' : '8.02x',
- 'title' : 'Electricity & Magnetism',
- 'xmlpath': '/802x/',
- 'github_url': 'https://github.com/MITx/8.02x',
- 'active' : True,
- 'default_chapter' : 'Introduction',
- 'default_section' : 'Introduction_%28Lewin_2002%29',
- },
- '6.189_Spring_2013': {'number' : '6.189x',
- 'title' : 'IAP Python Programming',
- 'xmlpath': '/6.189x/',
- 'github_url': 'https://github.com/MITx/6.189x',
- 'active' : True,
- 'default_chapter' : 'Week_1',
- 'default_section' : 'Variables_and_Binding',
- },
- '8.01_Fall_2012': {'number' : '8.01x',
- 'title' : 'Mechanics',
- 'xmlpath': '/8.01x/',
- 'github_url': 'https://github.com/MITx/8.01x',
- 'active': True,
- 'default_chapter' : 'Mechanics_Online_Spring_2012',
- 'default_section' : 'Introduction_to_the_course',
- 'location': 'i4x://edx/6002xs12/course/8.01_Fall_2012',
- },
- 'edx4edx': {'number' : 'edX.01',
- 'title' : 'edx4edx: edX Author Course',
- 'xmlpath': '/edx4edx/',
- 'github_url': 'https://github.com/MITx/edx4edx',
- 'active' : True,
- 'default_chapter' : 'Introduction',
- 'default_section' : 'edx4edx_Course',
- 'location': 'i4x://edx/6002xs12/course/edx4edx',
- },
- '7.03x_Fall_2012': {'number' : '7.03x',
- 'title' : 'Genetics',
- 'xmlpath': '/7.03x/',
- 'github_url': 'https://github.com/MITx/7.03x',
- 'active' : True,
- 'default_chapter' : 'Week_2',
- 'default_section' : 'ps1_question_1',
- },
- '3.091x_Fall_2012': {'number' : '3.091x',
- 'title' : 'Introduction to Solid State Chemistry',
- 'xmlpath': '/3.091x/',
- 'github_url': 'https://github.com/MITx/3.091x',
- 'active' : True,
- 'default_chapter' : 'Week_1',
- 'default_section' : 'Problem_Set_1',
- },
- '18.06x_Linear_Algebra': {'number' : '18.06x',
- 'title' : 'Linear Algebra',
- 'xmlpath': '/18.06x/',
- 'github_url': 'https://github.com/MITx/18.06x',
- 'default_chapter' : 'Unit_1',
- 'default_section' : 'Midterm_1',
- 'active' : True,
- },
- '6.00x_Fall_2012': {'number' : '6.00x',
- 'title' : 'Introduction to Computer Science and Programming',
- 'xmlpath': '/6.00x/',
- 'github_url': 'https://github.com/MITx/6.00x',
- 'active' : True,
- 'default_chapter' : 'Week_0',
- 'default_section' : 'Problem_Set_0',
- 'location': 'i4x://edx/6002xs12/course/6.00x_Fall_2012',
- },
- '7.00x_Fall_2012': {'number' : '7.00x',
- 'title' : 'Introduction to Biology',
- 'xmlpath': '/7.00x/',
- 'github_url': 'https://github.com/MITx/7.00x',
- 'active' : True,
- 'default_chapter' : 'Unit 1',
- 'default_section' : 'Introduction',
- },
- }
+LOGGING = get_logger_config(ENV_ROOT / "log",
+ logging_env="dev",
+ tracking_filename="tracking.log",
+ debug=True)
-#-----------------------------------------------------------------------------
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': ENV_ROOT / "db" / "mitx.db",
+ }
+}
-MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + (
- 'ssl_auth.ssl_auth.NginxProxyHeaderMiddleware', # ssl authentication behind nginx proxy
- )
+CACHES = {
+ # This is the cache used for most things. Askbot will not work without a
+ # functioning cache -- it relies on caching to load its settings in places.
+ # In staging/prod envs, the sessions also live here.
+ 'default': {
+ 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
+ 'LOCATION': 'mitx_loc_mem_cache',
+ 'KEY_FUNCTION': 'util.memcache.safe_key',
+ },
-AUTHENTICATION_BACKENDS = (
- 'ssl_auth.ssl_auth.SSLLoginBackend',
- 'django.contrib.auth.backends.ModelBackend',
- )
+ # The general cache is what you get if you use our util.cache. It's used for
+ # things like caching the course.xml file for different A/B test groups.
+ # We set it to be a DummyCache to force reloading of course.xml in dev.
+ # In staging environments, we would grab VERSION from data uploaded by the
+ # push process.
+ 'general': {
+ 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
+ 'KEY_PREFIX': 'general',
+ 'VERSION': 4,
+ 'KEY_FUNCTION': 'util.memcache.safe_key',
+ }
+}
-INSTALLED_APPS = INSTALLED_APPS + (
- 'ssl_auth',
- )
+# Dummy secret key for dev
+SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
-LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/'
-LOGIN_URL = MITX_ROOT_URL + '/'
+################################ OpenID Auth #################################
+MITX_FEATURES['AUTH_USE_OPENID'] = True
+
+INSTALLED_APPS += ('external_auth',)
+INSTALLED_APPS += ('django_openid_auth',)
+#INSTALLED_APPS += ('ssl_auth',)
+
+#MIDDLEWARE_CLASSES += (
+# #'ssl_auth.ssl_auth.NginxProxyHeaderMiddleware', # ssl authentication behind nginx proxy
+# )
+
+#AUTHENTICATION_BACKENDS = (
+# 'django_openid_auth.auth.OpenIDBackend',
+# 'django.contrib.auth.backends.ModelBackend',
+# )
+
+OPENID_CREATE_USERS = False
+OPENID_UPDATE_DETAILS_FROM_SREG = True
+OPENID_SSO_SERVER_URL = 'https://www.google.com/accounts/o8/id'
+OPENID_USE_AS_ADMIN_LOGIN = False
+#import external_auth.views as edXauth
+#OPENID_RENDER_FAILURE = edXauth.edXauth_openid
+
+################################ DEBUG TOOLBAR #################################
+INSTALLED_APPS += ('debug_toolbar',)
+MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
+INTERNAL_IPS = ('127.0.0.1',)
+
+DEBUG_TOOLBAR_PANELS = (
+ 'debug_toolbar.panels.version.VersionDebugPanel',
+ 'debug_toolbar.panels.timer.TimerDebugPanel',
+ 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
+ 'debug_toolbar.panels.headers.HeaderDebugPanel',
+ 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
+ 'debug_toolbar.panels.sql.SQLDebugPanel',
+ 'debug_toolbar.panels.signals.SignalDebugPanel',
+ 'debug_toolbar.panels.logger.LoggingPanel',
+
+# Enabling the profiler has a weird bug as of django-debug-toolbar==0.9.4 and
+# Django=1.3.1/1.4 where requests to views get duplicated (your method gets
+# hit twice). So you can uncomment when you need to diagnose performance
+# problems, but you shouldn't leave it on.
+# 'debug_toolbar.panels.profiling.ProfilingDebugPanel',
+)
+
+############################ FILE UPLOADS (ASKBOT) #############################
+DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
+MEDIA_ROOT = ENV_ROOT / "uploads"
+MEDIA_URL = "/static/uploads/"
+STATICFILES_DIRS.append(("uploads", MEDIA_ROOT))
+FILE_UPLOAD_TEMP_DIR = ENV_ROOT / "uploads"
+FILE_UPLOAD_HANDLERS = (
+ 'django.core.files.uploadhandler.MemoryFileUploadHandler',
+ 'django.core.files.uploadhandler.TemporaryFileUploadHandler',
+)
+
+########################### PIPELINE #################################
+
+PIPELINE_SASS_ARGUMENTS = '-r {proj_dir}/static/sass/bourbon/lib/bourbon.rb'.format(proj_dir=PROJECT_ROOT)
diff --git a/lms/templates/extauth_failure.html b/lms/templates/extauth_failure.html
new file mode 100644
index 0000000000..fa53ab1084
--- /dev/null
+++ b/lms/templates/extauth_failure.html
@@ -0,0 +1,11 @@
+
+
+
+ OpenID failed
+
+
+ OpenID failed
+ ${message}
+
+
diff --git a/lms/templates/index.html b/lms/templates/index.html
index d8b0394927..dcc18d4de1 100644
--- a/lms/templates/index.html
+++ b/lms/templates/index.html
@@ -144,3 +144,12 @@
+
+% if show_signup_immediately is not UNDEFINED:
+
+% endif
diff --git a/lms/templates/login_modal.html b/lms/templates/login_modal.html
index 393e76ee78..e2a2754226 100644
--- a/lms/templates/login_modal.html
+++ b/lms/templates/login_modal.html
@@ -27,6 +27,9 @@
Not enrolled? Sign up.
Forgot password?
+
+ login via openid
+
diff --git a/lms/templates/signup_modal.html b/lms/templates/signup_modal.html
index aef90ab0f2..346027418d 100644
--- a/lms/templates/signup_modal.html
+++ b/lms/templates/signup_modal.html
@@ -19,6 +19,7 @@
@@ -93,11 +106,13 @@
+ % if has_extauth_info is UNDEFINED:
+ % endif
diff --git a/lms/urls.py b/lms/urls.py
index 1c4a065e2b..8c36857ee3 100644
--- a/lms/urls.py
+++ b/lms/urls.py
@@ -160,12 +160,22 @@ if settings.DEBUG:
## Jasmine
urlpatterns=urlpatterns + (url(r'^_jasmine/', include('django_jasmine.urls')),)
+if settings.MITX_FEATURES.get('AUTH_USE_OPENID'):
+ urlpatterns += (
+ url(r'^openid/login/$', 'django_openid_auth.views.login_begin', name='openid-login'),
+ url(r'^openid/complete/$', 'external_auth.views.edXauth_openid_login_complete', name='openid-complete'),
+ url(r'^openid/logo.gif$', 'django_openid_auth.views.logo', name='openid-logo'),
+ )
+ urlpatterns += (
+ url(r'^extauth/$', 'external_auth.views.edXauth_signup', name='extauth-signup'),
+ )
+ # urlpatterns += (url(r'^openid/', include('django_openid_auth.urls')),)
+
urlpatterns = patterns(*urlpatterns)
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
-
#Custom error pages
handler404 = 'static_template_view.views.render_404'
handler500 = 'static_template_view.views.render_500'
diff --git a/requirements.txt b/requirements.txt
index 46c822642e..33b2bfeb05 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,6 +8,7 @@ lxml
boto
mako
python-memcached
+python-openid
path.py
django_debug_toolbar
-e git://github.com/MITx/django-pipeline.git#egg=django-pipeline
@@ -37,6 +38,7 @@ django-jasmine
django-keyedcache
django-mako
django-masquerade
+django-openid-auth
django-robots
django-ses
django-storages