Django

Code

Ticket #6083: 03-newforms-auth.diff

File 03-newforms-auth.diff, 18.6 kB (added by Petr Marhoun <petr.marhoun@gmail.com>, 10 months ago)
  • django/contrib/auth/newforms.py

    old new  
     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

    old new  
     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

    old new  
    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)