Commit aad6dc43 by ihoover

Merge pull request #417 from edx/ihoover/feature_flag_auto_auth

Ihoover/feature flag auto auth
parents c80a05f4 56883d65
......@@ -62,9 +62,6 @@ MITX_FEATURES = {
}
ENABLE_JASMINE = False
# needed to use lms student app
GENERATE_RANDOM_USER_CREDENTIALS = False
############################# SET PATH INFORMATION #############################
PROJECT_ROOT = path(__file__).abspath().dirname().dirname() # /mitx/cms
......@@ -108,9 +105,12 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.static',
'django.contrib.messages.context_processors.messages',
'django.contrib.auth.context_processors.auth', # this is required for admin
'django.core.context_processors.csrf', # necessary for csrf protection
)
# add csrf support unless disabled for load testing
if not MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING'):
TEMPLATE_CONTEXT_PROCESSORS += ('django.core.context_processors.csrf',) # necessary for csrf protection
LMS_BASE = None
#################### CAPA External Code Evaluation #############################
......@@ -142,7 +142,6 @@ MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'method_override.middleware.MethodOverrideMiddleware',
# Instead of AuthenticationMiddleware, we use a cache-backed version
......@@ -158,6 +157,10 @@ MIDDLEWARE_CLASSES = (
'django.middleware.transaction.TransactionMiddleware'
)
# add in csrf middleware unless disabled for load testing
if not MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING'):
MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + ('django.middleware.csrf.CsrfViewMiddleware',)
############################ SIGNAL HANDLERS ################################
# This is imported to register the exception signal handling that logs exceptions
import monitoring.exceptions # noqa
......
......@@ -149,6 +149,12 @@ if settings.MITX_FEATURES.get('ENABLE_SERVICE_STATUS'):
urlpatterns += (url(r'^admin/', include(admin.site.urls)),)
# enable automatic login
if settings.MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING'):
urlpatterns += (
url(r'^auto_auth$', 'student.views.auto_auth'),
)
urlpatterns = patterns(*urlpatterns)
# Custom error pages
......
from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User
from util.testing import UrlResetMixin
from mock import patch
from django.core.urlresolvers import reverse, NoReverseMatch
class AutoAuthEnabledTestCase(UrlResetMixin, TestCase):
"""
Tests for the Auto auth view that we have for load testing.
"""
@patch.dict("django.conf.settings.MITX_FEATURES", {"AUTOMATIC_AUTH_FOR_LOAD_TESTING": True})
def setUp(self):
# Patching the settings.MITX_FEATURES['AUTOMATIC_AUTH_FOR_LOAD_TESTING']
# value affects the contents of urls.py,
# so we need to call super.setUp() which reloads urls.py (because
# of the UrlResetMixin)
super(AutoAuthEnabledTestCase, self).setUp()
self.url = '/auto_auth'
self.cms_csrf_url = "signup"
self.lms_csrf_url = "signin_user"
self.client = Client()
def test_create_user(self):
"""
Test that user gets created when visiting the page.
"""
self.client.get(self.url)
qset = User.objects.all()
# assert user was created and is active
self.assertEqual(qset.count(), 1)
user = qset[0]
assert user.is_active
@patch('student.views.random.randint')
def test_create_multiple_users(self, randint):
"""
Test to make sure multiple users are created.
"""
randint.return_value = 1
self.client.get(self.url)
randint.return_value = 2
self.client.get(self.url)
qset = User.objects.all()
# make sure that USER_1 and USER_2 were created
self.assertEqual(qset.count(), 2)
@patch.dict("django.conf.settings.MITX_FEATURES", {"MAX_AUTO_AUTH_USERS": 1})
def test_login_already_created_user(self):
"""
Test that when we have reached the limit for automatic users
a subsequent request results in an already existant one being
logged in.
"""
# auto-generate 1 user (the max)
url = '/auto_auth'
self.client.get(url)
# go to the site again
self.client.get(url)
qset = User.objects.all()
# make sure it is the same user
self.assertEqual(qset.count(), 1)
class AutoAuthDisabledTestCase(UrlResetMixin, TestCase):
"""
Test that the page is inaccessible with default settings
"""
@patch.dict("django.conf.settings.MITX_FEATURES", {"AUTOMATIC_AUTH_FOR_LOAD_TESTING": False})
def setUp(self):
# Patching the settings.MITX_FEATURES['AUTOMATIC_AUTH_FOR_LOAD_TESTING']
# value affects the contents of urls.py,
# so we need to call super.setUp() which reloads urls.py (because
# of the UrlResetMixin)
super(AutoAuthDisabledTestCase, self).setUp()
self.url = '/auto_auth'
self.client = Client()
def test_auto_auth_disabled(self):
"""
Make sure automatic authentication is disabled.
"""
response = self.client.get(self.url)
self.assertEqual(response.status_code, 404)
def test_csrf_enabled(self):
"""
test that when not load testing, csrf protection is on
"""
cms_csrf_url = "signup"
lms_csrf_url = "signin_user"
self.client = Client(enforce_csrf_checks=True)
try:
csrf_protected_url = reverse(cms_csrf_url)
response = self.client.post(csrf_protected_url)
except NoReverseMatch:
csrf_protected_url = reverse(lms_csrf_url)
response = self.client.post(csrf_protected_url)
self.assertEqual(response.status_code, 403)
......@@ -19,6 +19,7 @@ from django.core.context_processors import csrf
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.core.validators import validate_email, validate_slug, ValidationError
from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError, transaction
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotAllowed, Http404
from django.shortcuts import redirect
......@@ -674,18 +675,20 @@ def create_account(request, post_override=None):
subject = ''.join(subject.splitlines())
message = render_to_string('emails/activation_email.txt', d)
try:
if settings.MITX_FEATURES.get('REROUTE_ACTIVATION_EMAIL'):
dest_addr = settings.MITX_FEATURES['REROUTE_ACTIVATION_EMAIL']
message = ("Activation for %s (%s): %s\n" % (user, user.email, profile.name) +
'-' * 80 + '\n\n' + message)
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [dest_addr], fail_silently=False)
elif not settings.GENERATE_RANDOM_USER_CREDENTIALS:
res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except:
log.warning('Unable to send activation email to user', exc_info=True)
js['value'] = _('Could not send activation e-mail.')
return HttpResponse(json.dumps(js))
# dont send email if we are doing load testing or random user generation for some reason
if not (settings.MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING')):
try:
if settings.MITX_FEATURES.get('REROUTE_ACTIVATION_EMAIL'):
dest_addr = settings.MITX_FEATURES['REROUTE_ACTIVATION_EMAIL']
message = ("Activation for %s (%s): %s\n" % (user, user.email, profile.name) +
'-' * 80 + '\n\n' + message)
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [dest_addr], fail_silently=False)
else:
res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except:
log.warning('Unable to send activation email to user', exc_info=True)
js['value'] = _('Could not send activation e-mail.')
return HttpResponse(json.dumps(js))
# Immediately after a user creates an account, we log them in. They are only
# logged in until they close the browser. They can't log in again until they click
......@@ -902,32 +905,51 @@ def create_exam_registration(request, post_override=None):
return HttpResponse(json.dumps(js), mimetype="application/json")
def get_random_post_override():
def auto_auth(request):
"""
Return a dictionary suitable for passing to post_vars of _do_create_account or post_override
of create_account, with random user info.
Automatically logs the user in with a generated random credentials
This view is only accessible when
settings.MITX_SETTINGS['AUTOMATIC_AUTH_FOR_LOAD_TESTING'] is true.
"""
def id_generator(size=6, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for x in range(size))
return {'username': "random_" + id_generator(),
'email': id_generator(size=10, chars=string.ascii_lowercase) + "_dummy_test@mitx.mit.edu",
'password': id_generator(),
'name': (id_generator(size=5, chars=string.ascii_lowercase) + " " +
id_generator(size=7, chars=string.ascii_lowercase)),
'honor_code': u'true',
'terms_of_service': u'true', }
def get_dummy_post_data(username, password):
"""
Return a dictionary suitable for passing to post_vars of _do_create_account or post_override
of create_account, with specified username and password.
"""
return {'username': username,
'email': username + "_dummy_test@mitx.mit.edu",
'password': password,
'name': username + " " + username,
'honor_code': u'true',
'terms_of_service': u'true', }
def create_random_account(create_account_function):
def inner_create_random_account(request):
return create_account_function(request, post_override=get_random_post_override())
# generate random user ceredentials from a small name space (determined by settings)
name_base = 'USER_'
pass_base = 'PASS_'
return inner_create_random_account
max_users = settings.MITX_FEATURES.get('MAX_AUTO_AUTH_USERS', 200)
number = random.randint(1, max_users)
# TODO (vshnayder): do we need GENERATE_RANDOM_USER_CREDENTIALS for anything?
if settings.GENERATE_RANDOM_USER_CREDENTIALS:
create_account = create_random_account(create_account)
username = name_base + str(number)
password = pass_base + str(number)
# if they already are a user, log in
try:
user = User.objects.get(username=username)
user = authenticate(username=username, password=password)
login(request, user)
# else create and activate account info
except ObjectDoesNotExist:
post_override = get_dummy_post_data(username, password)
create_account(request, post_override=post_override)
request.user.is_active = True
request.user.save()
# return empty success
return HttpResponse('')
@ensure_csrf_cookie
......
......@@ -178,6 +178,10 @@ for name, value in ENV_TOKENS.get("CODE_JAIL", {}).items():
COURSES_WITH_UNSAFE_CODE = ENV_TOKENS.get("COURSES_WITH_UNSAFE_CODE", [])
# automatic log in for load testing
MITX_FEATURES['AUTOMATIC_AUTH_FOR_LOAD_TESTING'] = ENV_TOKENS.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING')
MITX_FEATURES['MAX_AUTO_AUTH_USERS'] = ENV_TOKENS.get('MAX_AUTO_AUTH_USERS')
############################## SECURE AUTH ITEMS ###############
# Secret things: passwords, access keys, etc.
......
......@@ -37,7 +37,6 @@ PLATFORM_NAME = "edX"
COURSEWARE_ENABLED = True
ENABLE_JASMINE = False
GENERATE_RANDOM_USER_CREDENTIALS = False
PERFSTATS = False
DISCUSSION_SETTINGS = {
......@@ -145,6 +144,9 @@ MITX_FEATURES = {
# Allow use of the hint managment instructor view.
'ENABLE_HINTER_INSTRUCTOR_VIEW': False,
# for load testing
'AUTOMATIC_AUTH_FOR_LOAD_TESTING': False,
# Toggle to enable chat availability (configured on a per-course
# basis in Studio)
'ENABLE_CHAT': False
......@@ -218,7 +220,6 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.messages.context_processors.messages',
#'django.core.context_processors.i18n',
'django.contrib.auth.context_processors.auth', # this is required for admin
'django.core.context_processors.csrf', # necessary for csrf protection
# Added for django-wiki
'django.core.context_processors.media',
......@@ -231,6 +232,10 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'mitxmako.shortcuts.marketing_link_context_processor',
)
# add csrf support unless disabled for load testing
if not MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING'):
TEMPLATE_CONTEXT_PROCESSORS += ('django.core.context_processors.csrf',) # necessary for csrf protection
STUDENT_FILEUPLOAD_MAX_SIZE = 4 * 1000 * 1000 # 4 MB
MAX_FILEUPLOADS_PER_INPUT = 20
......@@ -469,7 +474,6 @@ MIDDLEWARE_CLASSES = (
'django_comment_client.middleware.AjaxExceptionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
# Instead of AuthenticationMiddleware, we use a cached backed version
#'django.contrib.auth.middleware.AuthenticationMiddleware',
......@@ -488,6 +492,10 @@ MIDDLEWARE_CLASSES = (
'codejail.django_integration.ConfigureCodeJailMiddleware',
)
# add in csrf middleware unless disabled for load testing
if not MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING'):
MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + ('django.middleware.csrf.CsrfViewMiddleware',)
############################### Pipeline #######################################
STATICFILES_STORAGE = 'pipeline.storage.PipelineCachedStorage'
......
......@@ -441,6 +441,12 @@ if settings.MITX_FEATURES.get('ENABLE_HINTER_INSTRUCTOR_VIEW'):
'instructor.hint_manager.hint_manager', name="hint_manager"),
)
# enable automatic login
if settings.MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING'):
urlpatterns += (
url(r'^auto_auth$', 'student.views.auto_auth'),
)
urlpatterns = patterns(*urlpatterns)
if settings.DEBUG:
......
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