Commit 69097e7b by Carson Gee

Merge pull request #2032 from carsongee/cg/ssl-studio

More fully integrate and test ssl external auth in CMS
parents 4dd2c42a 8cbe263c
......@@ -3,13 +3,13 @@ Public views
"""
from django_future.csrf import ensure_csrf_cookie
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.conf import settings
from edxmako.shortcuts import render_to_response
from external_auth.views import ssl_login_shortcut
from external_auth.views import ssl_login_shortcut, ssl_get_cert_from_request
from microsite_configuration.middleware import MicrositeConfiguration
__all__ = ['signup', 'login_page', 'howitworks']
......@@ -21,7 +21,14 @@ def signup(request):
Display the signup form.
"""
csrf_token = csrf(request)['csrf_token']
return render_to_response('signup.html', {'csrf': csrf_token})
if request.user.is_authenticated():
return redirect('/course')
if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'):
# Redirect to course to login to process their certificate if SSL is enabled
# and registration is disabled.
return redirect(reverse('login'))
return render_to_response('register.html', {'csrf': csrf_token})
@ssl_login_shortcut
......@@ -31,6 +38,12 @@ def login_page(request):
Display the login form.
"""
csrf_token = csrf(request)['csrf_token']
if (settings.FEATURES['AUTH_USE_CERTIFICATES'] and
ssl_get_cert_from_request(request)):
# SSL login doesn't require a login view, so redirect
# to course now that the user is authenticated via
# the decorator.
return redirect('/course')
return render_to_response(
'login.html',
{
......
......@@ -44,7 +44,7 @@ FEATURES = {
'ENABLE_DISCUSSION_SERVICE': False,
'AUTH_USE_MIT_CERTIFICATES': False,
'AUTH_USE_CERTIFICATES': False,
# email address for studio staff (eg to request course creation)
'STUDIO_REQUEST_EMAIL': '',
......
......@@ -9,7 +9,7 @@
from .common import *
from .dev import *
FEATURES['AUTH_USE_MIT_CERTIFICATES'] = True
FEATURES['AUTH_USE_CERTIFICATES'] = True
FEATURES['USE_DJANGO_PIPELINE'] = False # don't recompile scss
......
......@@ -146,6 +146,9 @@ CACHES = {
}
# Add external_auth to Installed apps for testing
INSTALLED_APPS += ('external_auth', )
# hide ratelimit warnings while running tests
filterwarnings('ignore', message='No request passed to the backend, unable to rate-limit')
......
......@@ -21,13 +21,14 @@ from edxmako.middleware import MakoMiddleware
from external_auth.models import ExternalAuthMap
import external_auth.views
from student.tests.factories import UserFactory
from xmodule.modulestore.exceptions import InsufficientSpecificationError
FEATURES_WITH_SSL_AUTH = settings.FEATURES.copy()
FEATURES_WITH_SSL_AUTH['AUTH_USE_MIT_CERTIFICATES'] = True
FEATURES_WITH_SSL_AUTH['AUTH_USE_CERTIFICATES'] = True
FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP = FEATURES_WITH_SSL_AUTH.copy()
FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP['AUTH_USE_MIT_CERTIFICATES_IMMEDIATE_SIGNUP'] = True
FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP['AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'] = True
FEATURES_WITHOUT_SSL_AUTH = settings.FEATURES.copy()
FEATURES_WITHOUT_SSL_AUTH['AUTH_USE_MIT_CERTIFICATES'] = False
FEATURES_WITHOUT_SSL_AUTH['AUTH_USE_CERTIFICATES'] = False
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH)
......@@ -90,15 +91,10 @@ class SSLClientTest(TestCase):
User.objects.get(email=self.USER_EMAIL)
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
@unittest.skip
def test_ssl_login_with_signup_cms(self):
"""
Validate that an SSL login creates an eamap user and
redirects them to the signup page on CMS.
This currently is failing and should be resolved to passing at
some point. using skip here instead of expectFailure because
of an issue with nose.
"""
self.client.get(
reverse('contentstore.views.login_page'),
......@@ -135,21 +131,19 @@ class SSLClientTest(TestCase):
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
@unittest.skip
def test_ssl_login_without_signup_cms(self):
"""
Test IMMEDIATE_SIGNUP feature flag and ensure the user account is
automatically created on CMS.
This currently is failing and should be resolved to passing at
some point. using skip here instead of expectFailure because
of an issue with nose.
automatically created on CMS, and that we are redirected
to courses.
"""
self.client.get(
response = self.client.get(
reverse('contentstore.views.login_page'),
SSL_CLIENT_S_DN=self.AUTH_DN.format(self.USER_NAME, self.USER_EMAIL)
)
self.assertEqual(response.status_code, 302)
self.assertIn('/course', response['location'])
# Assert our user exists in both eamap and Users, and that we are logged in
try:
......@@ -191,6 +185,27 @@ class SSLClientTest(TestCase):
self.assertIn(reverse('dashboard'), response['location'])
self.assertIn('_auth_user_id', self.client.session)
@unittest.skipUnless(settings.ROOT_URLCONF == 'cms.urls', 'Test only valid in cms')
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
def test_cms_registration_page_bypass(self):
"""
This tests to make sure when immediate signup is on that
the user doesn't get presented with the registration page.
"""
# Expect a NotImplementError from course page as we don't have anything else built
with self.assertRaisesRegexp(InsufficientSpecificationError,
'Must provide one of url, version_guid, package_id'):
self.client.get(
reverse('signup'), follow=True,
SSL_CLIENT_S_DN=self.AUTH_DN.format(self.USER_NAME, self.USER_EMAIL))
# assert that we are logged in
self.assertIn('_auth_user_id', self.client.session)
# Now that we are logged in, make sure we don't see the registration page
with self.assertRaisesRegexp(InsufficientSpecificationError,
'Must provide one of url, version_guid, package_id'):
self.client.get(reverse('signup'), follow=True)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
def test_signin_page_bypass(self):
......@@ -212,6 +227,7 @@ class SSLClientTest(TestCase):
self.assertIn(reverse('dashboard'), response['location'])
self.assertIn('_auth_user_id', self.client.session)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@override_settings(FEATURES=FEATURES_WITH_SSL_AUTH_IMMEDIATE_SIGNUP)
def test_ssl_bad_eamap(self):
......
......@@ -253,7 +253,7 @@ def _signup(request, eamap, retfun=None):
# save this for use by student.views.create_account
request.session['ExternalAuthMap'] = eamap
if settings.FEATURES.get('AUTH_USE_MIT_CERTIFICATES_IMMEDIATE_SIGNUP', ''):
if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP', ''):
# do signin immediately, by calling create_account, instead of asking
# student to fill in form. MIT students already have information filed.
username = eamap.external_email.split('@', 1)[0]
......@@ -362,7 +362,7 @@ def ssl_login_shortcut(fn):
call.
"""
if not settings.FEATURES['AUTH_USE_MIT_CERTIFICATES']:
if not settings.FEATURES['AUTH_USE_CERTIFICATES']:
return fn(*args, **kwargs)
request = args[0]
......@@ -394,7 +394,7 @@ def ssl_login_shortcut(fn):
def ssl_login(request):
"""
This is called by branding.views.index when
FEATURES['AUTH_USE_MIT_CERTIFICATES'] = True
FEATURES['AUTH_USE_CERTIFICATES'] = True
Used for MIT user authentication. This presumes the web server
(nginx) has been configured to require specific client
......@@ -408,7 +408,7 @@ def ssl_login(request):
Else continues on with student.views.index, and no authentication.
"""
# Just to make sure we're calling this only at MIT:
if not settings.FEATURES['AUTH_USE_MIT_CERTIFICATES']:
if not settings.FEATURES['AUTH_USE_CERTIFICATES']:
return HttpResponseForbidden()
cert = ssl_get_cert_from_request(request)
......
......@@ -330,7 +330,7 @@ def signin_user(request):
"""
This view will display the non-modal login form
"""
if (settings.FEATURES['AUTH_USE_MIT_CERTIFICATES'] and
if (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
......@@ -357,7 +357,7 @@ def register_user(request, extra_context=None):
"""
if request.user.is_authenticated():
return redirect(reverse('dashboard'))
if settings.FEATURES.get('AUTH_USE_MIT_CERTIFICATES_IMMEDIATE_SIGNUP'):
if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'):
# Redirect to branding to process their certificate if SSL is enabled
# and registration is disabled.
return redirect(reverse('root'))
......@@ -645,7 +645,7 @@ def accounts_login(request):
"""
if settings.FEATURES.get('AUTH_USE_CAS'):
return redirect(reverse('cas-login'))
if settings.FEATURES['AUTH_USE_MIT_CERTIFICATES']:
if settings.FEATURES['AUTH_USE_CERTIFICATES']:
# SSL login doesn't require a view, so redirect
# to branding and allow that to process the login.
return redirect(reverse('root'))
......
......@@ -23,7 +23,7 @@ def index(request):
if settings.COURSEWARE_ENABLED and request.user.is_authenticated():
return redirect(reverse('dashboard'))
if settings.FEATURES.get('AUTH_USE_MIT_CERTIFICATES'):
if settings.FEATURES.get('AUTH_USE_CERTIFICATES'):
from external_auth.views import ssl_login
return ssl_login(request)
......
......@@ -26,7 +26,7 @@ TEST_MONGODB_LOG = {
}
FEATURES_WITH_SSL_AUTH = settings.FEATURES.copy()
FEATURES_WITH_SSL_AUTH['AUTH_USE_MIT_CERTIFICATES'] = True
FEATURES_WITH_SSL_AUTH['AUTH_USE_CERTIFICATES'] = True
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
......
......@@ -160,7 +160,7 @@ class Users(SysadminDashboardView):
email_domain = getattr(settings, 'SSL_AUTH_EMAIL_DOMAIN', 'MIT.EDU')
msg = u''
if settings.FEATURES['AUTH_USE_MIT_CERTIFICATES']:
if settings.FEATURES['AUTH_USE_CERTIFICATES']:
if not '@' in uname:
email = '{0}@{1}'.format(uname, email_domain)
else:
......@@ -202,7 +202,7 @@ class Users(SysadminDashboardView):
profile.name = name
profile.save()
if settings.FEATURES['AUTH_USE_MIT_CERTIFICATES']:
if settings.FEATURES['AUTH_USE_CERTIFICATES']:
credential_string = getattr(settings, 'SSL_AUTH_DN_FORMAT_STRING',
'/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN={0}/emailAddress={1}')
credentials = credential_string.format(name, email)
......
......@@ -37,7 +37,7 @@ TEST_MONGODB_LOG = {
}
FEATURES_WITH_SSL_AUTH = settings.FEATURES.copy()
FEATURES_WITH_SSL_AUTH['AUTH_USE_MIT_CERTIFICATES'] = True
FEATURES_WITH_SSL_AUTH['AUTH_USE_CERTIFICATES'] = True
class SysadminBaseTestCase(ModuleStoreTestCase):
......
......@@ -8,7 +8,7 @@ Settings for the LMS that runs alongside the CMS on AWS
from ..dev import *
FEATURES['AUTH_USE_MIT_CERTIFICATES'] = False
FEATURES['AUTH_USE_CERTIFICATES'] = False
SUBDOMAIN_BRANDING['edge'] = 'edge'
SUBDOMAIN_BRANDING['preview.edge'] = 'edge'
......
......@@ -98,7 +98,7 @@ FEATURES = {
# extrernal access methods
'ACCESS_REQUIRE_STAFF_FOR_COURSE': False,
'AUTH_USE_OPENID': False,
'AUTH_USE_MIT_CERTIFICATES': False,
'AUTH_USE_CERTIFICATES': False,
'AUTH_USE_OPENID_PROVIDER': False,
# Even though external_auth is in common, shib assumes the LMS views / urls, so it should only be enabled
# in LMS
......
......@@ -202,7 +202,7 @@ OPENID_PROVIDER_TRUSTED_ROOTS = ['*']
######################## MIT Certificates SSL Auth ############################
FEATURES['AUTH_USE_MIT_CERTIFICATES'] = False
FEATURES['AUTH_USE_CERTIFICATES'] = False
################################# CELERY ######################################
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment