2011-06-27 00:51:34 +08:00
from django import forms
2011-12-10 06:10:03 +08:00
from django . forms . util import flatatt
2011-07-13 17:35:51 +08:00
from django . template import loader
2011-06-27 00:51:34 +08:00
from django . utils . http import int_to_base36
2011-12-10 06:10:03 +08:00
from django . utils . safestring import mark_safe
2011-06-27 00:51:34 +08:00
from django . utils . translation import ugettext_lazy as _
2011-07-13 17:35:51 +08:00
from django . contrib . auth . models import User
from django . contrib . auth . utils import UNUSABLE_PASSWORD
2006-06-29 00:37:02 +08:00
from django . contrib . auth import authenticate
2008-08-01 04:47:53 +08:00
from django . contrib . auth . tokens import default_token_generator
Fixed #14386, #8960, #10235, #10909, #10608, #13845, #14377 - standardize Site/RequestSite usage in various places.
Many thanks to gabrielhurley for putting most of this together. Also to
bmihelac, arthurk, qingfeng, hvendelbo, petr.pulc@s-cape.cz, Hraban for
reports and some initial patches.
The patch also contains some whitespace/PEP8 fixes.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@13980 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2010-10-04 22:20:47 +08:00
from django . contrib . sites . models import get_current_site
2011-06-27 00:51:34 +08:00
2011-12-10 06:10:03 +08:00
UNMASKED_DIGITS_TO_SHOW = 6
mask_password = lambda p : " %s %s " % ( p [ : UNMASKED_DIGITS_TO_SHOW ] , " * " * max ( len ( p ) - UNMASKED_DIGITS_TO_SHOW , 0 ) )
2011-12-16 00:12:46 +08:00
2011-12-10 06:10:03 +08:00
class ReadOnlyPasswordHashWidget ( forms . Widget ) :
def render ( self , name , value , attrs ) :
if not value :
return " None "
final_attrs = self . build_attrs ( attrs )
parts = value . split ( " $ " )
if len ( parts ) != 3 :
# Legacy passwords didn't specify a hash type and were md5.
hash_type = " md5 "
masked = mask_password ( value )
else :
hash_type = parts [ 0 ]
masked = mask_password ( parts [ 2 ] )
return mark_safe ( """ <div %(attrs)s >
< strong > % ( hash_type_label ) s < / strong > : % ( hash_type ) s
< strong > % ( masked_label ) s < / strong > : % ( masked ) s
< / div > """ % {
" attrs " : flatatt ( final_attrs ) ,
" hash_type_label " : _ ( " Hash type " ) ,
" hash_type " : hash_type ,
" masked_label " : _ ( " Masked hash " ) ,
" masked " : masked ,
} )
2011-12-16 00:12:46 +08:00
2011-12-10 06:10:03 +08:00
class ReadOnlyPasswordHashField ( forms . Field ) :
widget = ReadOnlyPasswordHashWidget
def __init__ ( self , * args , * * kwargs ) :
kwargs . setdefault ( " required " , False )
super ( ReadOnlyPasswordHashField , self ) . __init__ ( * args , * * kwargs )
2006-05-02 09:31:56 +08:00
2011-12-16 00:12:46 +08:00
2008-07-19 07:54:34 +08:00
class UserCreationForm ( forms . ModelForm ) :
"""
A form that creates a user , with no privileges , from the given username and password .
"""
2011-12-16 00:12:46 +08:00
error_messages = {
' duplicate_username ' : _ ( " A user with that username already exists. " ) ,
' password_mismatch ' : _ ( " The two password fields didn ' t match. " ) ,
}
2010-03-02 03:49:05 +08:00
username = forms . RegexField ( label = _ ( " Username " ) , max_length = 30 , regex = r ' ^[ \ w.@+-]+$ ' ,
help_text = _ ( " Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only. " ) ,
2010-03-15 20:16:01 +08:00
error_messages = { ' invalid ' : _ ( " This value may contain only letters, numbers and @/./+/-/_ characters. " ) } )
2008-08-02 05:18:17 +08:00
password1 = forms . CharField ( label = _ ( " Password " ) , widget = forms . PasswordInput )
2010-01-13 07:35:29 +08:00
password2 = forms . CharField ( label = _ ( " Password confirmation " ) , widget = forms . PasswordInput ,
help_text = _ ( " Enter the same password as above, for verification. " ) )
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
class Meta :
model = User
fields = ( " username " , )
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
def clean_username ( self ) :
2011-12-17 23:30:55 +08:00
# Since User.username is unique, this check is redundant,
# but it sets a nicer error message than the ORM. See #13147.
2008-07-19 07:54:34 +08:00
username = self . cleaned_data [ " username " ]
2006-08-04 12:18:12 +08:00
try :
2008-07-19 07:54:34 +08:00
User . objects . get ( username = username )
2006-08-04 12:18:12 +08:00
except User . DoesNotExist :
2008-07-19 07:54:34 +08:00
return username
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' duplicate_username ' ] )
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
def clean_password2 ( self ) :
2008-08-26 00:55:57 +08:00
password1 = self . cleaned_data . get ( " password1 " , " " )
2008-07-19 07:54:34 +08:00
password2 = self . cleaned_data [ " password2 " ]
if password1 != password2 :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' password_mismatch ' ] )
2008-07-19 07:54:34 +08:00
return password2
2008-08-06 00:36:20 +08:00
2010-01-12 10:29:45 +08:00
def save ( self , commit = True ) :
user = super ( UserCreationForm , self ) . save ( commit = False )
user . set_password ( self . cleaned_data [ " password1 " ] )
if commit :
user . save ( )
return user
2011-12-16 00:12:46 +08:00
2008-08-26 01:10:20 +08:00
class UserChangeForm ( forms . ModelForm ) :
2010-03-02 03:49:05 +08:00
username = forms . RegexField ( label = _ ( " Username " ) , max_length = 30 , regex = r ' ^[ \ w.@+-]+$ ' ,
help_text = _ ( " Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only. " ) ,
2010-03-15 20:16:01 +08:00
error_messages = { ' invalid ' : _ ( " This value may contain only letters, numbers and @/./+/-/_ characters. " ) } )
2011-12-10 06:10:03 +08:00
password = ReadOnlyPasswordHashField ( label = _ ( " Password " ) , help_text = _ ( " We don ' t store raw passwords, so there ' s no way to see this user ' s password, but you can change the password using <a href= \" password/ \" >this form</a>. " ) )
def clean_password ( self ) :
return self . initial [ " password " ]
2010-03-15 20:16:01 +08:00
2008-08-26 01:10:20 +08:00
class Meta :
model = User
2010-09-04 02:56:12 +08:00
def __init__ ( self , * args , * * kwargs ) :
super ( UserChangeForm , self ) . __init__ ( * args , * * kwargs )
2010-09-10 07:31:54 +08:00
f = self . fields . get ( ' user_permissions ' , None )
if f is not None :
f . queryset = f . queryset . select_related ( ' content_type ' )
2010-09-04 02:56:12 +08:00
2011-12-16 00:12:46 +08:00
2008-07-19 07:54:34 +08:00
class AuthenticationForm ( forms . Form ) :
2006-05-02 09:31:56 +08:00
"""
Base class for authenticating users . Extend this to get a form that accepts
username / password logins .
"""
2008-07-19 07:54:34 +08:00
username = forms . CharField ( label = _ ( " Username " ) , max_length = 30 )
2008-08-02 05:18:17 +08:00
password = forms . CharField ( label = _ ( " Password " ) , widget = forms . PasswordInput )
2008-08-06 00:36:20 +08:00
2011-12-16 00:12:46 +08:00
error_messages = {
' invalid_login ' : _ ( " Please enter a correct username and password. "
" Note that both fields are case-sensitive. " ) ,
' no_cookies ' : _ ( " Your Web browser doesn ' t appear to have cookies "
" enabled. Cookies are required for logging in. " ) ,
' inactive ' : _ ( " This account is inactive. " ) ,
}
2008-07-19 07:54:34 +08:00
def __init__ ( self , request = None , * args , * * kwargs ) :
2006-05-02 09:31:56 +08:00
"""
2008-07-19 07:54:34 +08:00
If request is passed in , the form will validate that cookies are
2006-05-02 09:31:56 +08:00
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
2008-07-19 07:54:34 +08:00
running this validation .
2006-05-02 09:31:56 +08:00
"""
self . request = request
self . user_cache = None
2008-07-19 07:54:34 +08:00
super ( AuthenticationForm , self ) . __init__ ( * args , * * kwargs )
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
def clean ( self ) :
username = self . cleaned_data . get ( ' username ' )
password = self . cleaned_data . get ( ' password ' )
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
if username and password :
self . user_cache = authenticate ( username = username , password = password )
if self . user_cache is None :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' invalid_login ' ] )
2008-07-19 07:54:34 +08:00
elif not self . user_cache . is_active :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' inactive ' ] )
2010-12-02 08:44:35 +08:00
self . check_for_test_cookie ( )
2008-07-19 07:54:34 +08:00
return self . cleaned_data
2008-08-06 00:36:20 +08:00
2010-12-02 08:44:35 +08:00
def check_for_test_cookie ( self ) :
if self . request and not self . request . session . test_cookie_worked ( ) :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' no_cookies ' ] )
2010-12-02 08:44:35 +08:00
2006-05-02 09:31:56 +08:00
def get_user_id ( self ) :
if self . user_cache :
return self . user_cache . id
return None
2008-08-06 00:36:20 +08:00
2006-05-02 09:31:56 +08:00
def get_user ( self ) :
return self . user_cache
2011-12-16 00:12:46 +08:00
2008-07-19 07:54:34 +08:00
class PasswordResetForm ( forms . Form ) :
2011-12-16 00:12:46 +08:00
error_messages = {
' unknown ' : _ ( " That e-mail address doesn ' t have an associated "
" user account. Are you sure you ' ve registered? " ) ,
' unusable ' : _ ( " The user account associated with this e-mail "
" address cannot reset the password. " ) ,
}
2008-07-31 17:08:55 +08:00
email = forms . EmailField ( label = _ ( " E-mail " ) , max_length = 75 )
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
def clean_email ( self ) :
"""
2011-04-02 00:10:22 +08:00
Validates that an active user exists with the given email address .
2008-07-19 07:54:34 +08:00
"""
email = self . cleaned_data [ " email " ]
2011-03-15 05:14:10 +08:00
self . users_cache = User . objects . filter (
email__iexact = email ,
2011-06-27 00:51:34 +08:00
is_active = True )
if not len ( self . users_cache ) :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' unknown ' ] )
2011-06-27 00:51:34 +08:00
if any ( ( user . password == UNUSABLE_PASSWORD ) for user in self . users_cache ) :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' unusable ' ] )
2009-02-27 01:11:28 +08:00
return email
2008-08-01 04:47:53 +08:00
2011-06-19 19:24:39 +08:00
def save ( self , domain_override = None ,
subject_template_name = ' registration/password_reset_subject.txt ' ,
email_template_name = ' registration/password_reset_email.html ' ,
use_https = False , token_generator = default_token_generator ,
from_email = None , request = None ) :
2008-07-19 07:54:34 +08:00
"""
2008-08-04 20:11:04 +08:00
Generates a one - use only link for resetting password and sends to the user
2008-07-19 07:54:34 +08:00
"""
2006-05-02 09:31:56 +08:00
from django . core . mail import send_mail
2007-06-20 04:04:54 +08:00
for user in self . users_cache :
if not domain_override :
Fixed #14386, #8960, #10235, #10909, #10608, #13845, #14377 - standardize Site/RequestSite usage in various places.
Many thanks to gabrielhurley for putting most of this together. Also to
bmihelac, arthurk, qingfeng, hvendelbo, petr.pulc@s-cape.cz, Hraban for
reports and some initial patches.
The patch also contains some whitespace/PEP8 fixes.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@13980 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2010-10-04 22:20:47 +08:00
current_site = get_current_site ( request )
2007-06-20 04:04:54 +08:00
site_name = current_site . name
domain = current_site . domain
else :
site_name = domain = domain_override
c = {
' email ' : user . email ,
' domain ' : domain ,
' site_name ' : site_name ,
2008-08-01 04:47:53 +08:00
' uid ' : int_to_base36 ( user . id ) ,
2007-06-20 04:04:54 +08:00
' user ' : user ,
2008-08-01 04:47:53 +08:00
' token ' : token_generator . make_token ( user ) ,
' protocol ' : use_https and ' https ' or ' http ' ,
2008-07-19 07:54:34 +08:00
}
2011-06-19 19:24:39 +08:00
subject = loader . render_to_string ( subject_template_name , c )
# Email subject *must not* contain newlines
subject = ' ' . join ( subject . splitlines ( ) )
email = loader . render_to_string ( email_template_name , c )
send_mail ( subject , email , from_email , [ user . email ] )
2006-05-02 09:31:56 +08:00
2011-12-16 00:12:46 +08:00
2008-08-01 04:47:53 +08:00
class SetPasswordForm ( forms . Form ) :
2008-07-19 07:54:34 +08:00
"""
2008-08-01 04:47:53 +08:00
A form that lets a user change set his / her password without
entering the old password
2008-07-19 07:54:34 +08:00
"""
2011-12-16 00:12:46 +08:00
error_messages = {
' password_mismatch ' : _ ( " The two password fields didn ' t match. " ) ,
}
2008-08-02 05:18:17 +08:00
new_password1 = forms . CharField ( label = _ ( " New password " ) , widget = forms . PasswordInput )
new_password2 = forms . CharField ( label = _ ( " New password confirmation " ) , widget = forms . PasswordInput )
2008-08-01 04:47:53 +08:00
2008-07-19 07:54:34 +08:00
def __init__ ( self , user , * args , * * kwargs ) :
2006-05-02 09:31:56 +08:00
self . user = user
2008-08-01 04:47:53 +08:00
super ( SetPasswordForm , self ) . __init__ ( * args , * * kwargs )
2008-07-19 07:54:34 +08:00
def clean_new_password2 ( self ) :
password1 = self . cleaned_data . get ( ' new_password1 ' )
password2 = self . cleaned_data . get ( ' new_password2 ' )
if password1 and password2 :
if password1 != password2 :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' password_mismatch ' ] )
2008-07-19 07:54:34 +08:00
return password2
2008-08-01 04:47:53 +08:00
2008-07-19 07:54:34 +08:00
def save ( self , commit = True ) :
self . user . set_password ( self . cleaned_data [ ' new_password1 ' ] )
if commit :
self . user . save ( )
return self . user
2008-08-06 00:36:20 +08:00
2011-12-16 00:12:46 +08:00
2008-08-01 04:47:53 +08:00
class PasswordChangeForm ( SetPasswordForm ) :
"""
A form that lets a user change his / her password by entering
their old password .
"""
2011-12-16 00:12:46 +08:00
error_messages = dict ( SetPasswordForm . error_messages , * * {
' password_incorrect ' : _ ( " Your old password was entered incorrectly. Please enter it again. " ) ,
} )
2008-08-02 05:18:17 +08:00
old_password = forms . CharField ( label = _ ( " Old password " ) , widget = forms . PasswordInput )
2008-08-06 00:36:20 +08:00
2008-08-01 04:47:53 +08:00
def clean_old_password ( self ) :
"""
Validates that the old_password field is correct .
"""
old_password = self . cleaned_data [ " old_password " ]
if not self . user . check_password ( old_password ) :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' password_incorrect ' ] )
2008-08-01 04:47:53 +08:00
return old_password
PasswordChangeForm . base_fields . keyOrder = [ ' old_password ' , ' new_password1 ' , ' new_password2 ' ]
2008-08-06 00:36:20 +08:00
2011-12-16 00:12:46 +08:00
2008-07-19 07:54:34 +08:00
class AdminPasswordChangeForm ( forms . Form ) :
"""
A form used to change the password of a user in the admin interface .
"""
2011-12-16 00:12:46 +08:00
error_messages = {
' password_mismatch ' : _ ( " The two password fields didn ' t match. " ) ,
}
2008-08-02 05:18:17 +08:00
password1 = forms . CharField ( label = _ ( " Password " ) , widget = forms . PasswordInput )
password2 = forms . CharField ( label = _ ( " Password (again) " ) , widget = forms . PasswordInput )
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
def __init__ ( self , user , * args , * * kwargs ) :
2006-12-30 15:16:25 +08:00
self . user = user
2008-07-19 07:54:34 +08:00
super ( AdminPasswordChangeForm , self ) . __init__ ( * args , * * kwargs )
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
def clean_password2 ( self ) :
password1 = self . cleaned_data . get ( ' password1 ' )
password2 = self . cleaned_data . get ( ' password2 ' )
if password1 and password2 :
if password1 != password2 :
2011-12-16 00:12:46 +08:00
raise forms . ValidationError ( self . error_messages [ ' password_mismatch ' ] )
2008-07-19 07:54:34 +08:00
return password2
2008-08-06 00:36:20 +08:00
2008-07-19 07:54:34 +08:00
def save ( self , commit = True ) :
"""
Saves the new password .
"""
self . user . set_password ( self . cleaned_data [ " password1 " ] )
if commit :
self . user . save ( )
return self . user