Commit c176a7fe by Victor Shnayder

Add account creation and login to CMS

* connect up views from student app
* Add initial @login_required decorators on the protected views
* Add tests for some of the basic functionality
parent 5033ec65
......@@ -4,45 +4,165 @@ from django.test import TestCase
from mock import patch, Mock
from override_settings import override_settings
from django.conf import settings
from django.core.urlresolvers import reverse
from student.models import Registration
from django.contrib.auth.models import User
def parse_json(response):
"""Parse response, which is assumed to be json"""
return json.loads(response.content)
class AuthTestCase(TestCase):
"""Check that various permissions-related things work"""
def test_index(self):
"""Make sure the main page loads."""
resp = self.client.get('/')
self.assertEqual(resp.status_code, 200)
def user(email):
'''look up a user by email'''
return User.objects.get(email=email)
def test_signup_load(self):
"""Make sure the signup page loads."""
resp = self.client.get('/signup')
self.assertEqual(resp.status_code, 200)
def registration(email):
'''look up registration object by email'''
return Registration.objects.get(user__email=email)
class AuthTestCase(TestCase):
"""Check that various permissions-related things work"""
def test_create_account(self):
def setUp(self):
self.email = 'a@b.com'
self.pw = 'xyz'
self.username = 'testuser'
def check_page_get(self, url, expected):
resp = self.client.get(url)
self.assertEqual(resp.status_code, expected)
return resp
def test_public_pages_load(self):
"""Make sure pages that don't require login load without error."""
pages = (
reverse('login'),
reverse('signup'),
)
for page in pages:
print "Checking '{0}'".format(page)
self.check_page_get(page, 200)
def test_create_account_errors(self):
# No post data -- should fail
resp = self.client.post('/create_account', {})
self.assertEqual(resp.status_code, 200)
data = parse_json(resp)
self.assertEqual(data['success'], False)
# Should work
def _create_account(self, username, email, pw):
'''Try to create an account. No error checking'''
resp = self.client.post('/create_account', {
'username': 'user',
'email': 'a@b.com',
'password': 'xyz',
'username': username,
'email': email,
'password': pw,
'location' : 'home',
'language' : 'Franglish',
'name' : 'Fred Weasley',
'terms_of_service' : 'true',
'honor_code' : 'true'})
return resp
def create_account(self, username, email, pw):
'''Create the account and check that it worked'''
resp = self._create_account(username, email, pw)
self.assertEqual(resp.status_code, 200)
data = parse_json(resp)
self.assertEqual(data['success'], True)
# Check both that the user is created, and inactive
self.assertFalse(user(self.email).is_active)
return resp
def _activate_user(self, email):
'''look up the user's activation key in the db, then hit the activate view.
No error checking'''
activation_key = registration(email).activation_key
# and now we try to activate
resp = self.client.get(reverse('activate', kwargs={'key': activation_key}))
return resp
def activate_user(self, email):
resp = self._activate_user(email)
self.assertEqual(resp.status_code, 200)
# Now make sure that the user is now actually activated
self.assertTrue(user(self.email).is_active)
def test_create_account(self):
self.create_account(self.username, self.email, self.pw)
self.activate_user(self.email)
def _login(self, email, pw):
'''Login. View should always return 200. The success/fail is in the
returned json'''
resp = self.client.post(reverse('login_post'),
{'email': email, 'password': pw})
self.assertEqual(resp.status_code, 200)
return resp
def login(self, email, pw):
'''Login, check that it worked.'''
resp = self._login(self.email, self.pw)
data = parse_json(resp)
self.assertTrue(data['success'])
return resp
def test_login(self):
self.create_account(self.username, self.email, self.pw)
# Not activated yet. Login should fail.
resp = self._login(self.email, self.pw)
data = parse_json(resp)
self.assertFalse(data['success'])
self.activate_user(self.email)
# Now login should work
self.login(self.email, self.pw)
def test_private_pages_auth(self):
"""Make sure pages that do require login work."""
auth_pages = (
reverse('index'),
reverse('edit_item'),
reverse('save_item'),
)
# These are pages that should just load when the user is logged in
# (no data needed)
simple_auth_pages = (
reverse('index'),
)
# need an activated user
self.test_create_account()
# Not logged in. Should redirect to login.
print 'Not logged in'
for page in auth_pages:
print "Checking '{0}'".format(page)
self.check_page_get(page, expected=302)
# Logged in should work.
self.login(self.email, self.pw)
print 'Logged in'
for page in simple_auth_pages:
print "Checking '{0}'".format(page)
self.check_page_get(page, expected=200)
def test_index_auth(self):
# not logged in. Should return a redirect.
resp = self.client.get(reverse('index'))
self.assertEqual(resp.status_code, 302)
# Logged in should work.
......@@ -2,6 +2,7 @@ from util.json_request import expect_json
import json
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf
from django_future.csrf import ensure_csrf_cookie
from django.core.urlresolvers import reverse
......@@ -13,7 +14,7 @@ from github_sync import export_to_github
from mitxmako.shortcuts import render_to_response
from xmodule.modulestore.django import modulestore
@login_required
@ensure_csrf_cookie
def index(request):
courses = modulestore().get_items(['i4x', None, None, 'course', None])
......@@ -36,13 +37,23 @@ def signup(request):
return render_to_response('signup.html', {'csrf': csrf_token })
@ensure_csrf_cookie
def login_page(request):
"""
Display the login form.
"""
csrf_token = csrf(request)['csrf_token']
return render_to_response('login.html', {'csrf': csrf_token })
@login_required
@ensure_csrf_cookie
def course_index(request, org, course, name):
# TODO (cpennington): These need to be read in from the active user
course = modulestore().get_item(['i4x', org, course, 'course', name])
weeks = course.get_children()
return render_to_response('course_index.html', {'weeks': weeks})
@login_required
def edit_item(request):
item_id = request.GET['id']
item = modulestore().get_item(item_id)
......@@ -54,6 +65,7 @@ def edit_item(request):
})
@login_required
@expect_json
def save_item(request):
item_id = request.POST['id']
......
......@@ -70,6 +70,10 @@ TEMPLATE_DIRS = (
MITX_ROOT_URL = ''
LOGIN_REDIRECT_URL = MITX_ROOT_URL + '/login'
LOGIN_URL = MITX_ROOT_URL + '/login'
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.core.context_processors.static',
......
......@@ -7,8 +7,7 @@
<section class="activation">
<h1>Account already active!</h1>
<p> This account has already been activated. You can log in at
the <a href="/">home page</a>.</p>
<p> This account has already been activated. <a href="/login">Log in here</a>.</p>
</div>
</section>
......
......@@ -6,7 +6,7 @@
<section class="tos">
<div>
<h1>Activation Complete!</h1>
<p>Thanks for activating your account. You can log in at the <a href="/">home page</a>.</p>
<p>Thanks for activating your account. <a href="/login">Log in here</a>.</p>
</div>
</section>
......
<form name="login" action="login", method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf_token}"/>
<%inherit file="base.html" />
<%! from django.core.urlresolvers import reverse %>
<%block name="title">Log in</%block>
% if next is not None:
<input type="hidden" name="next" value="${next}"/>
% endif
<%block name="content">
Username: <input type="text" name="username" />
Possword: <input type="password" name="password" />
<input type="submit" value="Submit" />
</form>
<section class="main-container">
<section class="main-content">
<header>
<h3>Log in</h3>
<hr>
</header>
<form id="login_form" action="login_post" method="post">
<label>E-mail</label>
<input name="email" type="email" placeholder="E-mail">
<label>Password</label>
<input name="password" type="password" placeholder="Password">
<label class="remember-me">
<input name="remember" type="checkbox">
Remember me
</label>
<div class="submit">
<input name="submit" type="submit" value="Submit">
</div>
</form>
<section class="login-extra">
<p>
<span>Not enrolled? <a href="#">Sign up.</a></span>
<a href="#" class="pwd-reset">Forgot password?</a>
</p>
</section>
</section>
</section>
<script type="text/javascript">
(function() {
function getCookie(name) {
return $.cookie(name);
}
function postJSON(url, data, callback) {
$.ajax({type:'POST',
url: url,
dataType: 'json',
data: data,
success: callback,
headers : {'X-CSRFToken':getCookie('csrftoken')}
});
}
$('form#login_form').submit(function(e) {
e.preventDefault();
var submit_data = $('#login_form').serialize();
postJSON('/login_post',
submit_data,
function(json) {
if(json.success) {
location.href="${reverse('index')}";
} else if($('#login_error').length == 0) {
$('#login_form').prepend('<div id="login_error">Email or password is incorrect.</div>');
} else {
$('#login_error').stop().css("background-color", "#933").animate({ backgroundColor: "#333"}, 2000);
}
}
);
});
})(this)
</script>
</%block>
......@@ -11,16 +11,25 @@ urlpatterns = ('',
url(r'^$', 'contentstore.views.index', name='index'),
url(r'^edit_item$', 'contentstore.views.edit_item', name='edit_item'),
url(r'^save_item$', 'contentstore.views.save_item', name='save_item'),
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$', 'contentstore.views.course_index', name='course_index'),
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$',
'contentstore.views.course_index', name='course_index'),
url(r'^github_service_hook$', 'github_sync.views.github_post_receive'),
)
# User creation and updating views
urlpatterns += (
url(r'^signup$', 'contentstore.views.signup'),
url(r'^signup$', 'contentstore.views.signup', name='signup'),
url(r'^create_account$', 'student.views.create_account'),
url(r'^activate/(?P<key>[^/]*)$', 'student.views.activate_account'),
url(r'^activate/(?P<key>[^/]*)$', 'student.views.activate_account', name='activate'),
# form page
url(r'^login$', 'contentstore.views.login_page', name='login'),
# ajax view that actually does the work
url(r'^login_post$', 'student.views.login_user', name='login_post'),
url(r'^logout$', 'student.views.logout_user', name='logout'),
)
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