First working version of the code.
This commit is contained in:
parent
74e9744bcf
commit
face026842
4 changed files with 142 additions and 0 deletions
password_required
24
password_required/decorators.py
Normal file
24
password_required/decorators.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
""" The password_required decorator for Django views """
|
||||
from functools import update_wrapper, wraps
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils.decorators import available_attrs
|
||||
from django.utils.http import urlquote
|
||||
|
||||
def password_required(view_func=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||
"""
|
||||
Decorator for views that checks that the user has entered the password,
|
||||
redirecting to the log-in page if necessary.
|
||||
"""
|
||||
def _wrapped_view(request, *args, **kwargs):
|
||||
if request.session.get('password_required_auth', False):
|
||||
return view_func(request, *args, **kwargs)
|
||||
|
||||
return HttpResponseRedirect('%s?%s=%s' % (
|
||||
reverse('password_required.views.login'),
|
||||
redirect_field_name,
|
||||
urlquote(request.get_full_path()),
|
||||
))
|
||||
return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view)
|
||||
|
45
password_required/forms.py
Normal file
45
password_required/forms.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
class AuthenticationForm(forms.Form):
|
||||
"""
|
||||
Simple form to allow users to access a page via a password.
|
||||
|
||||
A copy of django.contrib.auth.forms.AuthenticationForm, adapted to this
|
||||
much simpler use case.
|
||||
"""
|
||||
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
|
||||
|
||||
def __init__(self, request=None, *args, **kwargs):
|
||||
"""
|
||||
If request is passed in, the form will validate that cookies are
|
||||
enabled. Note that the request (a HttpRequest object) must have set a
|
||||
cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before
|
||||
running this validation.
|
||||
"""
|
||||
self.request = request
|
||||
super(AuthenticationForm, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Validate that the password entered was correct.
|
||||
"""
|
||||
password = self.cleaned_data.get('password')
|
||||
correct_password = getattr(settings, 'PASSWORD_REQUIRED_PASSWORD', None)
|
||||
|
||||
if not correct_password:
|
||||
raise forms.ValidationError(_("PASSWORD_REQUIRED_PASSWORD is not set, and thus it is currently impossible to log in."))
|
||||
|
||||
if not (password == correct_password or
|
||||
password.strip() == correct_password):
|
||||
raise forms.ValidationError(_("Please enter the correct password. Note that the password is case-sensitive."))
|
||||
|
||||
# TODO: determine whether this should move to its own method.
|
||||
if self.request:
|
||||
if not self.request.session.test_cookie_worked():
|
||||
raise forms.ValidationError(_("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in."))
|
||||
|
||||
return self.cleaned_data
|
||||
|
6
password_required/templates/password_required_login.html
Normal file
6
password_required/templates/password_required_login.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
{% load i18n %}
|
||||
<form id="password-required-login" action="" method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="{% trans "Let me in" %}" />
|
||||
</form>
|
||||
|
67
password_required/views.py
Normal file
67
password_required/views.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
from django.conf import settings
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.contrib.sites.models import Site, RequestSite
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from password_required.forms import AuthenticationForm
|
||||
|
||||
@csrf_protect
|
||||
@never_cache
|
||||
def login(request, template_name='password_required_login.html',
|
||||
redirect_field_name=REDIRECT_FIELD_NAME,
|
||||
authentication_form=AuthenticationForm):
|
||||
"""Displays the login form and handles the login action."""
|
||||
|
||||
redirect_to = _clean_redirect(request.REQUEST.get(redirect_field_name, ''))
|
||||
|
||||
if request.method == "POST":
|
||||
form = authentication_form(data=request.POST)
|
||||
if form.is_valid():
|
||||
# Mark the user as logged in via his session data.
|
||||
request.session['password_required_auth'] = True
|
||||
|
||||
if request.session.test_cookie_worked():
|
||||
request.session.delete_test_cookie()
|
||||
|
||||
return HttpResponseRedirect(redirect_to)
|
||||
|
||||
else:
|
||||
form = authentication_form(request)
|
||||
|
||||
request.session.set_test_cookie()
|
||||
|
||||
if Site._meta.installed:
|
||||
current_site = Site.objects.get_current()
|
||||
else:
|
||||
current_site = RequestSite(request)
|
||||
|
||||
return render_to_response(template_name, {
|
||||
'form': form,
|
||||
redirect_field_name: redirect_to,
|
||||
'site': current_site,
|
||||
'site_name': current_site.name,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def _clean_redirect(redirect_to):
|
||||
"""
|
||||
Perform a few security checks on the redirect destination.
|
||||
|
||||
Copied from django.contrib.auth.views.login. It really should be split
|
||||
out from that.
|
||||
"""
|
||||
# Light security check -- make sure redirect_to isn't garbage.
|
||||
if not redirect_to or ' ' in redirect_to:
|
||||
redirect_to = settings.LOGIN_REDIRECT_URL
|
||||
|
||||
# Heavier security check -- redirects to http://example.com should
|
||||
# not be allowed, but things like /view/?param=http://example.com
|
||||
# should be allowed. This regex checks if there is a '//' *before* a
|
||||
# question mark.
|
||||
elif '//' in redirect_to and re.match(r'[^\?]*//', redirect_to):
|
||||
redirect_to = settings.LOGIN_REDIRECT_URL
|
||||
|
||||
return redirect_to
|
||||
|
Reference in a new issue