parent
35cecb1ebd
commit
5848bea9dc
|
@ -226,6 +226,7 @@ class AdminSite(object):
|
||||||
# Admin-site-wide views.
|
# Admin-site-wide views.
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$', wrap(self.index), name='index'),
|
url(r'^$', wrap(self.index), name='index'),
|
||||||
|
url(r'^login/$', self.login, name='login'),
|
||||||
url(r'^logout/$', wrap(self.logout), name='logout'),
|
url(r'^logout/$', wrap(self.logout), name='logout'),
|
||||||
url(r'^password_change/$', wrap(self.password_change, cacheable=True), name='password_change'),
|
url(r'^password_change/$', wrap(self.password_change, cacheable=True), name='password_change'),
|
||||||
url(r'^password_change/done/$', wrap(self.password_change_done, cacheable=True), name='password_change_done'),
|
url(r'^password_change/done/$', wrap(self.password_change_done, cacheable=True), name='password_change_done'),
|
||||||
|
@ -337,6 +338,8 @@ class AdminSite(object):
|
||||||
title=_('Log in'),
|
title=_('Log in'),
|
||||||
app_path=request.get_full_path(),
|
app_path=request.get_full_path(),
|
||||||
)
|
)
|
||||||
|
if (REDIRECT_FIELD_NAME not in request.GET and
|
||||||
|
REDIRECT_FIELD_NAME not in request.POST):
|
||||||
context[REDIRECT_FIELD_NAME] = request.get_full_path()
|
context[REDIRECT_FIELD_NAME] = request.get_full_path()
|
||||||
context.update(extra_context or {})
|
context.update(extra_context or {})
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,14 @@
|
||||||
from functools import wraps
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from django.contrib.admin.forms import AdminAuthenticationForm
|
|
||||||
from django.contrib.auth.views import login
|
|
||||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
|
|
||||||
|
|
||||||
def staff_member_required(view_func):
|
def staff_member_required(view_func, redirect_field_name=REDIRECT_FIELD_NAME, login_url='admin:login'):
|
||||||
"""
|
"""
|
||||||
Decorator for views that checks that the user is logged in and is a staff
|
Decorator for views that checks that the user is logged in and is a staff
|
||||||
member, displaying the login page if necessary.
|
member, displaying the login page if necessary.
|
||||||
"""
|
"""
|
||||||
@wraps(view_func)
|
return user_passes_test(
|
||||||
def _checklogin(request, *args, **kwargs):
|
lambda u: u.is_active and u.is_staff,
|
||||||
if request.user.is_active and request.user.is_staff:
|
login_url=login_url,
|
||||||
# The user is valid. Continue to the admin page.
|
redirect_field_name=redirect_field_name
|
||||||
return view_func(request, *args, **kwargs)
|
)(view_func)
|
||||||
|
|
||||||
assert hasattr(request, 'session'), "The Django admin requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
|
|
||||||
defaults = {
|
|
||||||
'template_name': 'admin/login.html',
|
|
||||||
'authentication_form': AdminAuthenticationForm,
|
|
||||||
'extra_context': {
|
|
||||||
'title': _('Log in'),
|
|
||||||
'app_path': request.get_full_path(),
|
|
||||||
REDIRECT_FIELD_NAME: request.get_full_path(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return login(request, **defaults)
|
|
||||||
return _checklogin
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class AdminDocViewTests(TestCase):
|
||||||
|
|
||||||
def test_index(self):
|
def test_index(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
response = self.client.get(reverse('django-admindocs-docroot'))
|
response = self.client.get(reverse('django-admindocs-docroot'), follow=True)
|
||||||
# Should display the login screen
|
# Should display the login screen
|
||||||
self.assertContains(response,
|
self.assertContains(response,
|
||||||
'<input type="hidden" name="next" value="/admindocs/" />', html=True)
|
'<input type="hidden" name="next" value="/admindocs/" />', html=True)
|
||||||
|
|
|
@ -1297,11 +1297,11 @@ class AdminViewPermissionsTest(TestCase):
|
||||||
superuser.is_active = False
|
superuser.is_active = False
|
||||||
superuser.save()
|
superuser.save()
|
||||||
|
|
||||||
response = self.client.get('/test_admin/admin/')
|
response = self.client.get('/test_admin/admin/', follow=True)
|
||||||
self.assertContains(response, 'id="login-form"')
|
self.assertContains(response, 'id="login-form"')
|
||||||
self.assertNotContains(response, 'Log out')
|
self.assertNotContains(response, 'Log out')
|
||||||
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
response = self.client.get('/test_admin/admin/secure-view/', follow=True)
|
||||||
self.assertContains(response, 'id="login-form"')
|
self.assertContains(response, 'id="login-form"')
|
||||||
|
|
||||||
def testDisabledStaffPermissionsWhenLoggedIn(self):
|
def testDisabledStaffPermissionsWhenLoggedIn(self):
|
||||||
|
@ -1310,11 +1310,11 @@ class AdminViewPermissionsTest(TestCase):
|
||||||
superuser.is_staff = False
|
superuser.is_staff = False
|
||||||
superuser.save()
|
superuser.save()
|
||||||
|
|
||||||
response = self.client.get('/test_admin/admin/')
|
response = self.client.get('/test_admin/admin/', follow=True)
|
||||||
self.assertContains(response, 'id="login-form"')
|
self.assertContains(response, 'id="login-form"')
|
||||||
self.assertNotContains(response, 'Log out')
|
self.assertNotContains(response, 'Log out')
|
||||||
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
response = self.client.get('/test_admin/admin/secure-view/', follow=True)
|
||||||
self.assertContains(response, 'id="login-form"')
|
self.assertContains(response, 'id="login-form"')
|
||||||
|
|
||||||
def testAppIndexFailEarly(self):
|
def testAppIndexFailEarly(self):
|
||||||
|
@ -1338,6 +1338,25 @@ class AdminViewPermissionsTest(TestCase):
|
||||||
response = self.client.get('/test_admin/admin/admin_views/')
|
response = self.client.get('/test_admin/admin/admin_views/')
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_shortcut_view_only_available_to_staff(self):
|
||||||
|
"""
|
||||||
|
Only admin users should be able to use the admin shortcut view.
|
||||||
|
"""
|
||||||
|
model_ctype = ContentType.objects.get_for_model(ModelWithStringPrimaryKey)
|
||||||
|
obj = ModelWithStringPrimaryKey.objects.create(string_pk='foo')
|
||||||
|
shortcut_url = "/test_admin/admin/r/%s/%s/" % (model_ctype.pk, obj.pk)
|
||||||
|
|
||||||
|
# Not logged in: we should see the login page.
|
||||||
|
response = self.client.get(shortcut_url, follow=False)
|
||||||
|
self.assertTemplateUsed(response, 'admin/login.html')
|
||||||
|
|
||||||
|
# Logged in? Redirect.
|
||||||
|
self.client.login(username='super', password='secret')
|
||||||
|
response = self.client.get(shortcut_url, follow=False)
|
||||||
|
# Can't use self.assertRedirects() because User.get_absolute_url() is silly.
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response.url, 'http://example.com/dummy/foo/')
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
class AdminViewsNoUrlTest(TestCase):
|
class AdminViewsNoUrlTest(TestCase):
|
||||||
|
@ -1625,162 +1644,25 @@ class AdminViewStringPrimaryKeyTest(TestCase):
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
class SecureViewTests(TestCase):
|
class SecureViewTests(TestCase):
|
||||||
|
"""
|
||||||
|
Test behaviour of a view protected by the staff_member_required decorator.
|
||||||
|
"""
|
||||||
urls = "admin_views.urls"
|
urls = "admin_views.urls"
|
||||||
fixtures = ['admin-views-users.xml']
|
fixtures = ['admin-views-users.xml']
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
# login POST dicts
|
|
||||||
self.super_login = {
|
|
||||||
LOGIN_FORM_KEY: 1,
|
|
||||||
REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
|
|
||||||
'username': 'super',
|
|
||||||
'password': 'secret',
|
|
||||||
}
|
|
||||||
self.super_email_login = {
|
|
||||||
LOGIN_FORM_KEY: 1,
|
|
||||||
REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
|
|
||||||
'username': 'super@example.com',
|
|
||||||
'password': 'secret',
|
|
||||||
}
|
|
||||||
self.super_email_bad_login = {
|
|
||||||
LOGIN_FORM_KEY: 1,
|
|
||||||
REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
|
|
||||||
'username': 'super@example.com',
|
|
||||||
'password': 'notsecret',
|
|
||||||
}
|
|
||||||
self.adduser_login = {
|
|
||||||
LOGIN_FORM_KEY: 1,
|
|
||||||
REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
|
|
||||||
'username': 'adduser',
|
|
||||||
'password': 'secret',
|
|
||||||
}
|
|
||||||
self.changeuser_login = {
|
|
||||||
LOGIN_FORM_KEY: 1,
|
|
||||||
REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
|
|
||||||
'username': 'changeuser',
|
|
||||||
'password': 'secret',
|
|
||||||
}
|
|
||||||
self.deleteuser_login = {
|
|
||||||
LOGIN_FORM_KEY: 1,
|
|
||||||
REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
|
|
||||||
'username': 'deleteuser',
|
|
||||||
'password': 'secret',
|
|
||||||
}
|
|
||||||
self.joepublic_login = {
|
|
||||||
LOGIN_FORM_KEY: 1,
|
|
||||||
REDIRECT_FIELD_NAME: '/test_admin/admin/secure-view/',
|
|
||||||
'username': 'joepublic',
|
|
||||||
'password': 'secret',
|
|
||||||
}
|
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
||||||
def test_secure_view_shows_login_if_not_logged_in(self):
|
def test_secure_view_shows_login_if_not_logged_in(self):
|
||||||
"Ensure that we see the login form"
|
"""
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
Ensure that we see the admin login form.
|
||||||
|
"""
|
||||||
|
secure_url = '/test_admin/admin/secure-view/'
|
||||||
|
response = self.client.get(secure_url)
|
||||||
|
self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), secure_url))
|
||||||
|
response = self.client.get(secure_url, follow=True)
|
||||||
self.assertTemplateUsed(response, 'admin/login.html')
|
self.assertTemplateUsed(response, 'admin/login.html')
|
||||||
|
self.assertEqual(response.context[REDIRECT_FIELD_NAME], secure_url)
|
||||||
def test_secure_view_login_successfully_redirects_to_original_url(self):
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
query_string = 'the-answer=42'
|
|
||||||
redirect_url = '/test_admin/admin/secure-view/?%s' % query_string
|
|
||||||
new_next = {REDIRECT_FIELD_NAME: redirect_url}
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', dict(self.super_login, **new_next), QUERY_STRING=query_string)
|
|
||||||
self.assertRedirects(login, redirect_url)
|
|
||||||
|
|
||||||
def test_staff_member_required_decorator_works_as_per_admin_login(self):
|
|
||||||
"""
|
|
||||||
Make sure only staff members can log in.
|
|
||||||
|
|
||||||
Successful posts to the login page will redirect to the orignal url.
|
|
||||||
Unsuccessfull attempts will continue to render the login page with
|
|
||||||
a 200 status code.
|
|
||||||
"""
|
|
||||||
# Super User
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', self.super_login)
|
|
||||||
self.assertRedirects(login, '/test_admin/admin/secure-view/')
|
|
||||||
self.assertFalse(login.context)
|
|
||||||
self.client.get('/test_admin/admin/logout/')
|
|
||||||
# make sure the view removes test cookie
|
|
||||||
self.assertEqual(self.client.session.test_cookie_worked(), False)
|
|
||||||
|
|
||||||
# Test if user enters email address
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', self.super_email_login)
|
|
||||||
self.assertContains(login, ERROR_MESSAGE)
|
|
||||||
# only correct passwords get a username hint
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', self.super_email_bad_login)
|
|
||||||
self.assertContains(login, ERROR_MESSAGE)
|
|
||||||
new_user = User(username='jondoe', password='secret', email='super@example.com')
|
|
||||||
new_user.save()
|
|
||||||
# check to ensure if there are multiple email addresses a user doesn't get a 500
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', self.super_email_login)
|
|
||||||
self.assertContains(login, ERROR_MESSAGE)
|
|
||||||
|
|
||||||
# Add User
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', self.adduser_login)
|
|
||||||
self.assertRedirects(login, '/test_admin/admin/secure-view/')
|
|
||||||
self.assertFalse(login.context)
|
|
||||||
self.client.get('/test_admin/admin/logout/')
|
|
||||||
|
|
||||||
# Change User
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', self.changeuser_login)
|
|
||||||
self.assertRedirects(login, '/test_admin/admin/secure-view/')
|
|
||||||
self.assertFalse(login.context)
|
|
||||||
self.client.get('/test_admin/admin/logout/')
|
|
||||||
|
|
||||||
# Delete User
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', self.deleteuser_login)
|
|
||||||
self.assertRedirects(login, '/test_admin/admin/secure-view/')
|
|
||||||
self.assertFalse(login.context)
|
|
||||||
self.client.get('/test_admin/admin/logout/')
|
|
||||||
|
|
||||||
# Regular User should not be able to login.
|
|
||||||
response = self.client.get('/test_admin/admin/secure-view/')
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
login = self.client.post('/test_admin/admin/secure-view/', self.joepublic_login)
|
|
||||||
self.assertEqual(login.status_code, 200)
|
|
||||||
# Login.context is a list of context dicts we just need to check the first one.
|
|
||||||
self.assertContains(login, ERROR_MESSAGE)
|
|
||||||
|
|
||||||
# 8509 - if a normal user is already logged in, it is possible
|
|
||||||
# to change user into the superuser without error
|
|
||||||
login = self.client.login(username='joepublic', password='secret')
|
|
||||||
# Check and make sure that if user expires, data still persists
|
|
||||||
self.client.get('/test_admin/admin/secure-view/')
|
|
||||||
self.client.post('/test_admin/admin/secure-view/', self.super_login)
|
|
||||||
# make sure the view removes test cookie
|
|
||||||
self.assertEqual(self.client.session.test_cookie_worked(), False)
|
|
||||||
|
|
||||||
def test_shortcut_view_only_available_to_staff(self):
|
|
||||||
"""
|
|
||||||
Only admin users should be able to use the admin shortcut view.
|
|
||||||
"""
|
|
||||||
model_ctype = ContentType.objects.get_for_model(ModelWithStringPrimaryKey)
|
|
||||||
obj = ModelWithStringPrimaryKey.objects.create(string_pk='foo')
|
|
||||||
shortcut_url = "/test_admin/admin/r/%s/%s/" % (model_ctype.pk, obj.pk)
|
|
||||||
|
|
||||||
# Not logged in: we should see the login page.
|
|
||||||
response = self.client.get(shortcut_url, follow=False)
|
|
||||||
self.assertTemplateUsed(response, 'admin/login.html')
|
|
||||||
|
|
||||||
# Logged in? Redirect.
|
|
||||||
self.client.login(username='super', password='secret')
|
|
||||||
response = self.client.get(shortcut_url, follow=False)
|
|
||||||
# Can't use self.assertRedirects() because User.get_absolute_url() is silly.
|
|
||||||
self.assertEqual(response.status_code, 302)
|
|
||||||
self.assertEqual(response.url, 'http://example.com/dummy/foo/')
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
|
|
Loading…
Reference in New Issue