Ticket #6083: 03-newforms-auth.diff
File 03-newforms-auth.diff, 18.6 KB (added by , 17 years ago) |
---|
-
django/contrib/auth/newforms.py
=== added file 'django/contrib/auth/newforms.py'
1 import base64, md5 2 import cPickle as pickle 3 4 from django.conf import settings 5 from django.contrib.auth import authenticate 6 from django.contrib.auth.models import User 7 from django.contrib.sites.models import Site 8 from django import newforms as forms 9 from django.template import Context, loader 10 from django.utils.translation import ugettext as _ 11 12 class UserCreationForm(forms.ModelForm): 13 "A form that creates a user, with no privileges, from the given username and password." 14 # TODO: Model level validation should simplify this form. 15 username = forms.RegexField(label=_('Username'), max_length=30, regex=r'^\w+$', 16 error_message=_("This value must contain only letters, numbers and underscores.")) 17 password = forms.CharField(label=_('Password'), max_length=60, widget=forms.PasswordInput) 18 password2 = forms.CharField(label=_('Password confirmation'), max_length=60, widget=forms.PasswordInput) 19 20 class Meta: 21 model = User 22 fields = 'username', 'password', 'password2', 23 24 def clean_username(self): 25 username = self.cleaned_data.get('username') 26 if username: 27 if User.objects.filter(username=username): 28 raise forms.ValidationError(_("A user with that username already exists.")) 29 return username 30 31 def clean_password2(self): 32 password = self.cleaned_data.get('password') 33 password2 = self.cleaned_data.get('password2') 34 if password and password2: 35 if password != password2: 36 raise forms.ValidationError(_("The two password fields didn't match.")) 37 return password2 38 39 def save(self, commit=True): 40 user = super(UserCreationForm, self).save(commit=False) 41 user.set_password(self.cleaned_data['password']) 42 if commit: 43 user.save() 44 return user 45 46 class AuthenticationForm(forms.Form): 47 """ 48 Base class for authenticating users. 49 """ 50 username = forms.CharField(max_length=30) 51 password = forms.CharField(widget=forms.PasswordInput, max_length=30) 52 53 def __init__(self, request=None, *args, **kwargs): 54 self.request = request 55 self.user_cache = None 56 if self.request: 57 self.request.session.set_test_cookie() 58 super(AuthenticationForm, self).__init__(*args, **kwargs) 59 60 def clean(self): 61 username = self.cleaned_data.get('username') 62 password = self.cleaned_data.get('password') 63 if username and password: 64 self.user_cache = authenticate(username=username, password=password) 65 if self.user_cache is None: 66 raise forms.ValidationError(_("Please enter a correct username and password. Note that both fields are case-sensitive.")) 67 if not self.user_cache.is_active: 68 raise forms.ValidationError(_("This account is inactive.")) 69 if self.request: 70 if not self.request.session.test_cookie_worked(): 71 raise forms.ValidationError(_("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.")) 72 self.request.session.delete_test_cookie() 73 return self.cleaned_data 74 75 def get_user_id(self): 76 if self.user_cache: 77 return self.user_cache.id 78 else: 79 return None 80 81 def get_user(self): 82 return self.user_cache 83 84 class RedirectionAuthenticationForm(AuthenticationForm): 85 """ 86 Authentication form with field for redirection url. 87 """ 88 redirect_to = forms.CharField(required=False, widget=forms.HiddenInput) 89 90 def initial_redirect_to(request, redirect_field_name): 91 return request.REQUEST.get(redirect_field_name) 92 initial_redirect_to = staticmethod(initial_redirect_to) 93 94 def clean_redirect_to(self): 95 redirect_to = self.cleaned_data.get('redirect_to') 96 # Light security check -- make sure redirect_to isn't garbage. 97 if not redirect_to or '//' in redirect_to or ' ' in redirect_to: 98 return settings.LOGIN_REDIRECT_URL 99 else: 100 return redirect_to 101 102 class PostDataAuthenticationForm(AuthenticationForm): 103 """ 104 Authentication form with field for post data. 105 """ 106 post_data = forms.CharField(required=False, widget=forms.HiddenInput) 107 LOGIN_FORM_KEY = forms.CharField(required=False, widget=forms.HiddenInput) 108 109 def initial_post_data(request): 110 pickled = pickle.dumps(request.POST) 111 pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() 112 return base64.encodestring(pickled + pickled_md5) 113 initial_post_data = staticmethod(initial_post_data) 114 115 def clean_post_data(self): 116 post_data = self.cleaned_data.get('post_data') 117 if post_data: 118 encoded_data = base64.decodestring(post_data) 119 pickled, pickled_md5 = encoded_data[:-32], encoded_data[-32:] 120 if md5.new(pickled + settings.SECRET_KEY).hexdigest() != pickled_md5: 121 # User may have tampered with session cookie. 122 return None 123 return pickle.loads(pickled) 124 else: 125 return None 126 127 class PasswordChangeForm(forms.Form): 128 "A form that lets a user change his password." 129 old_password = forms.CharField(widget=forms.PasswordInput, max_length=30) 130 new_password = forms.CharField(widget=forms.PasswordInput, max_length=30) 131 new_password2 = forms.CharField(widget=forms.PasswordInput, max_length=30) 132 133 def __init__(self, user, *args, **kwargs): 134 self.user = user 135 super(PasswordChangeForm, self).__init__(*args, **kwargs) 136 137 def clean_old_password(self): 138 old_password = self.cleaned_data.get('old_password') 139 if old_password: 140 if not self.user.check_password(old_password): 141 raise forms.ValidationError(_("Your old password was entered incorrectly. Please enter it again.")) 142 return old_password 143 144 def clean_password2(self): 145 new_password = self.cleaned_data.get('new_password') 146 new_password2 = self.cleaned_data.get('new_password2') 147 if new_password and new_password2: 148 if new_password != new_password2: 149 raise forms.ValidationError(_("The two 'new password' fields didn't match.")) 150 return new_password2 151 152 def save(self, commit=True): 153 self.user.set_password(self.cleaned_data['new_password']) 154 if commit: 155 return self.user.save() 156 return self.user 157 158 class PasswordResetForm(forms.Form): 159 "A form that lets a user request a password reset" 160 email = forms.EmailField() 161 162 def clean_email(self): 163 "Validates that a user exists with the given e-mail address" 164 email = self.cleaned_data.get('email') 165 if email: 166 self.users_cache = User.objects.filter(email__iexact=email) 167 if len(self.users_cache) == 0: 168 raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?")) 169 170 def save(self, email_template_name='registration/password_reset_email.html', domain_override=None): 171 "Calculates a new password randomly and sends it to the user" 172 from django.core.mail import send_mail 173 for user in self.users_cache: 174 new_pass = User.objects.make_random_password() 175 user.set_password(new_pass) 176 user.save() 177 if not domain_override: 178 current_site = Site.objects.get_current() 179 site_name = current_site.name 180 domain = current_site.domain 181 else: 182 site_name = domain = domain_override 183 t = loader.get_template(email_template_name) 184 c = { 185 'new_password': new_pass, 186 'email': user.email, 187 'domain': domain, 188 'site_name': site_name, 189 'user': user, 190 } 191 send_mail(_('Password reset on %s') % site_name, t.render(Context(c)), None, [user.email]) -
django/contrib/auth/newviews.py
=== added file 'django/contrib/auth/newviews.py'
1 from django.contrib.auth import REDIRECT_FIELD_NAME 2 from django.contrib.auth.newforms import RedirectionAuthenticationForm 3 from django.contrib.auth.newforms import PostDataAuthenticationForm 4 from django.contrib.auth.newforms import PasswordChangeForm 5 from django.contrib.auth.newforms import PasswordResetForm 6 from django.http import HttpResponseRedirect 7 from django.shortcuts import render_to_response 8 from django.template import RequestContext 9 10 def _get_context(request, extra_context=None, **kwargs): 11 context = RequestContext(request) 12 context.update(kwargs) 13 if extra_context: 14 context.update(extra_context) 15 return context 16 17 def login(request, template_name='registration/login.html', redirect_field_name=REDIRECT_FIELD_NAME, extra_context=None): 18 "Displays the login form and handles the login action." 19 if request.method == 'POST': 20 form = RedirectionAuthenticationForm(request, request.POST) 21 if form.is_valid(): 22 from django.contrib.auth import login 23 login(request, form.get_user()) 24 return HttpResponseRedirect(form.cleaned_data['redirect_to']) 25 else: 26 initial = {'redirect_to': RedirectionAuthenticationForm.initial_redirect_to(request, redirect_field_name)} 27 form = RedirectionAuthenticationForm(request, initial=initial) 28 return render_to_response(template_name, context_instance=_get_context(request, extra_context, form=form)) 29 30 def login_with_post_data(request, view_func, view_func_args, view_func_kwargs, template_name='registration/login.html', extra_context=None): 31 if request.method == 'POST': 32 if 'LOGIN_FORM_KEY' in request.POST: 33 form = PostDataAuthenticationForm(request, request.POST) 34 if form.is_valid(): 35 from django.contrib.auth import login 36 login(request, form.get_user()) 37 request.POST = form.cleaned_data['post_data'] 38 return view_func(request, *view_func_args, **view_func_kwargs) 39 else: 40 initial = {'post_data': PostDataAuthenticationForm.initial_post_data(request)} 41 form = PostDataAuthenticationForm(request, initial=initial) 42 else: 43 form = PostDataAuthenticationForm(request) 44 return render_to_response(template_name, context_instance=_get_context(request, extra_context, form=form)) 45 46 def logout(request, template_name='registration/logged_out.html', next_page=None, extra_context=None): 47 "Logs out the user and displays 'You are logged out' message." 48 from django.contrib.auth import logout 49 logout(request) 50 if next_page is None: 51 return render_to_response(template_name, context_instance=_get_context(request, extra_context)) 52 else: 53 # Redirect to this page until the session has been cleared. 54 return HttpResponseRedirect(next_page or request.path) 55 56 def logout_then_login(request, login_url=None): 57 "Logs out the user if he is logged in. Then redirects to the log-in page." 58 if not login_url: 59 from django.conf import settings 60 login_url = settings.LOGIN_URL 61 return logout(request, next_page=login_url) 62 63 def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): 64 "Redirects the user to the login page, passing the given 'next' page" 65 if not login_url: 66 from django.conf import settings 67 login_url = settings.LOGIN_URL 68 return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, next)) 69 70 def password_change(request, template_name='registration/password_change_form.html', next_page=None, extra_context=None): 71 if request.method == 'POST': 72 form = PasswordChangeForm(request.user, request.POST) 73 if form.is_valid(): 74 form.save() 75 return HttpResponseRedirect(next_page or '%sdone/' % request.path) 76 else: 77 form = PasswordChangeForm(request.user) 78 context = RequestContext(request) 79 return render_to_response(template_name, context_instance=_get_context(request, extra_context, form=form)) 80 81 def password_change_done(request, template_name='registration/password_change_done.html', extra_context=None): 82 return render_to_response(template_name, context_instance=_get_context(request, extra_context)) 83 84 def password_reset(request, template_name='registration/password_reset_form.html', next_page=None, extra_context=None, 85 email_template_name='registration/password_reset_email.html', domain_override=None): 86 if request.method == 'POST': 87 form = PasswordResetForm(request.POST) 88 if form.is_valid(): 89 form.save(email_template_name=email_template_name, domain_override=domain_override) 90 return HttpResponseRedirect(next_page or '%sdone/' % request.path) 91 else: 92 form = PasswordResetForm() 93 return render_to_response(template_name, context_instance=_get_context(request, extra_context, form=form)) 94 95 def password_reset_done(request, template_name='registration/password_reset_done.html', extra_context=None): 96 return render_to_response(template_name, context_instance=_get_context(request, extra_context)) -
django/contrib/auth/decorators.py
=== modified file 'django/contrib/auth/decorators.py'
2 2 from django.http import HttpResponseRedirect 3 3 from django.utils.http import urlquote 4 4 5 def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): 5 def user_passes_test(test_func, login_url=None, redirect_field_name=None, 6 post_data=False, login_func=None, **login_func_kwargs): 6 7 """ 7 8 Decorator for views that checks that the user passes the given test, 8 9 redirecting to the log-in page if necessary. The test should be a callable 9 10 that takes the user object and returns True if the user passes. 11 If post data is True, there is no redirection but login funcion is used. 10 12 """ 11 13 def decorate(view_func): 12 return _CheckLogin(view_func, test_func, login_url, redirect_field_name) 14 return _CheckLogin(view_func, test_func, login_url, redirect_field_name, 15 post_data, login_func, **login_func_kwargs) 13 16 return decorate 14 17 15 def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME): 18 def login_required(view_func=None, login_url=None, redirect_field_name=None, 19 post_data=False, login_func=None, **login_func_kwargs): 16 20 """ 17 21 Decorator for views that checks that the user is logged in, redirecting 18 22 to the log-in page if necessary. 19 23 """ 20 actual_decorator = user_passes_test( 21 lambda u: u.is_authenticated(), 22 redirect_field_name=redirect_field_name 23 ) 24 if function: 25 return actual_decorator(function) 26 return actual_decorator 27 28 def permission_required(perm, login_url=None): 24 actual_decorator = user_passes_test(lambda u: u.is_authenticated(), login_url, 25 redirect_field_name, post_data, login_func, **login_func_kwargs) 26 if view_func: 27 return actual_decorator(view_func) 28 else: 29 return actual_decorator 30 31 def staff_required(view_func=None, login_url=None, redirect_field_name=None, 32 post_data=False, login_func=None, **login_func_kwargs): 33 """ 34 Decorator for views that checks that the user is staff, redirecting 35 to the log-in page if necessary. 36 """ 37 actual_decorator = user_passes_test(lambda u: u.is_staff, login_url, 38 redirect_field_name, post_data, login_func, **login_func_kwargs) 39 if view_func: 40 return actual_decorator(view_func) 41 else: 42 return actual_decorator 43 44 def permission_required(perm, login_url=None, redirect_field_name=None, 45 post_data=False, login_func=None, **login_func_kwargs): 29 46 """ 30 47 Decorator for views that checks whether a user has a particular permission 31 48 enabled, redirecting to the log-in page if necessary. 32 49 """ 33 return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url) 50 return user_passes_test(lambda u: u.has_perm(perm), login_url, 51 redirect_field_name, post_data, login_func, **login_func_kwargs) 34 52 35 53 class _CheckLogin(object): 36 54 """ … … 43 61 _CheckLogin object is used as a method decorator, the view function 44 62 is properly bound to its instance. 45 63 """ 46 def __init__(self, view_func, test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): 64 def __init__(self, view_func, test_func, login_url=None, redirect_field_name=None, 65 post_data=False, login_func=None, **login_func_kwargs): 47 66 if not login_url: 48 67 from django.conf import settings 49 68 login_url = settings.LOGIN_URL 69 if not redirect_field_name: 70 redirect_field_name = REDIRECT_FIELD_NAME 71 if not login_func: 72 from django.contrib.auth.newviews import login_with_post_data 73 login_func = login_with_post_data 50 74 self.view_func = view_func 51 75 self.test_func = test_func 52 76 self.login_url = login_url 53 77 self.redirect_field_name = redirect_field_name 78 self.post_data = post_data 79 self.login_func = login_func 80 self.login_func_kwargs = login_func_kwargs 54 81 self.__name__ = view_func.__name__ 55 82 56 83 def __get__(self, obj, cls=None): 57 84 view_func = self.view_func.__get__(obj, cls) 58 return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name) 59 85 return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name, 86 self.post_data, self.login_func, self.login_func_kwargs) 87 60 88 def __call__(self, request, *args, **kwargs): 61 89 if self.test_func(request.user): 62 90 return self.view_func(request, *args, **kwargs) 63 path = urlquote(request.get_full_path()) 64 tup = self.login_url, self.redirect_field_name, path 65 return HttpResponseRedirect('%s?%s=%s' % tup) 91 else: 92 if self.post_data: 93 return self.login_func(request, self.view_func, args, kwargs, **self.login_func_kwargs) 94 else: 95 path = urlquote(request.get_full_path()) 96 tup = self.login_url, self.redirect_field_name, path 97 return HttpResponseRedirect('%s?%s=%s' % tup)