Ticket #6083: 03-newforms-auth.diff

File 03-newforms-auth.diff, 18.6 KB (added by Petr Marhoun <petr.marhoun@…>, 8 years ago)
  • django/contrib/auth/newforms.py

    === added file 'django/contrib/auth/newforms.py'
     
     1import base64, md5
     2import cPickle as pickle
     3
     4from django.conf import settings
     5from django.contrib.auth import authenticate
     6from django.contrib.auth.models import User
     7from django.contrib.sites.models import Site
     8from django import newforms as forms
     9from django.template import Context, loader
     10from django.utils.translation import ugettext as _
     11
     12class 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
     46class 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
     84class 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
     102class 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
     127class 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
     158class 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'
     
     1from django.contrib.auth import REDIRECT_FIELD_NAME
     2from django.contrib.auth.newforms import RedirectionAuthenticationForm
     3from django.contrib.auth.newforms import PostDataAuthenticationForm
     4from django.contrib.auth.newforms import PasswordChangeForm
     5from django.contrib.auth.newforms import PasswordResetForm
     6from django.http import HttpResponseRedirect
     7from django.shortcuts import render_to_response
     8from django.template import RequestContext
     9
     10def _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
     17def 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
     30def 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
     46def 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
     56def 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
     63def 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
     70def 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
     81def 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
     84def 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
     95def 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'
     
    22from django.http import HttpResponseRedirect
    33from django.utils.http import urlquote
    44
    5 def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
     5def user_passes_test(test_func, login_url=None, redirect_field_name=None,
     6        post_data=False, login_func=None, **login_func_kwargs):
    67    """
    78    Decorator for views that checks that the user passes the given test,
    89    redirecting to the log-in page if necessary. The test should be a callable
    910    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.
    1012    """
    1113    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)
    1316    return decorate
    1417
    15 def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
     18def login_required(view_func=None, login_url=None, redirect_field_name=None,
     19        post_data=False, login_func=None, **login_func_kwargs):
    1620    """
    1721    Decorator for views that checks that the user is logged in, redirecting
    1822    to the log-in page if necessary.
    1923    """
    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
     31def 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
     44def permission_required(perm, login_url=None, redirect_field_name=None,
     45        post_data=False, login_func=None, **login_func_kwargs):
    2946    """
    3047    Decorator for views that checks whether a user has a particular permission
    3148    enabled, redirecting to the log-in page if necessary.
    3249    """
    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)
    3452
    3553class _CheckLogin(object):
    3654    """
     
    4361    _CheckLogin object is used as a method decorator, the view function
    4462    is properly bound to its instance.
    4563    """
    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):
    4766        if not login_url:
    4867            from django.conf import settings
    4968            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
    5074        self.view_func = view_func
    5175        self.test_func = test_func
    5276        self.login_url = login_url
    5377        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
    5481        self.__name__ = view_func.__name__
    55        
     82
    5683    def __get__(self, obj, cls=None):
    5784        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
    6088    def __call__(self, request, *args, **kwargs):
    6189        if self.test_func(request.user):
    6290            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)
Back to Top