Fixed #273 -- BACKWARDS-INCOMPATIBLE CHANGE -- Changed auth.User.password field to add support for other password encryption algorithms. Renamed password_md5 to password and changed field length from 32 to 128. See http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges for upgrade information

git-svn-id: http://code.djangoproject.com/svn/django/trunk@1327 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2005-11-21 03:33:22 +00:00
parent f1a8869339
commit a49fa746cd
2 changed files with 56 additions and 13 deletions

View File

@ -34,7 +34,7 @@ class User(meta.Model):
first_name = meta.CharField(_('first name'), maxlength=30, blank=True) first_name = meta.CharField(_('first name'), maxlength=30, blank=True)
last_name = meta.CharField(_('last name'), maxlength=30, blank=True) last_name = meta.CharField(_('last name'), maxlength=30, blank=True)
email = meta.EmailField(_('e-mail address'), blank=True) email = meta.EmailField(_('e-mail address'), blank=True)
password_md5 = meta.CharField(_('password'), maxlength=32, help_text=_("Use an MD5 hash -- not the raw password.")) password = meta.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]"))
is_staff = meta.BooleanField(_('staff status'), help_text=_("Designates whether the user can log into this admin site.")) is_staff = meta.BooleanField(_('staff status'), help_text=_("Designates whether the user can log into this admin site."))
is_active = meta.BooleanField(_('active'), default=True) is_active = meta.BooleanField(_('active'), default=True)
is_superuser = meta.BooleanField(_('superuser status')) is_superuser = meta.BooleanField(_('superuser status'))
@ -53,7 +53,7 @@ class User(meta.Model):
exceptions = ('SiteProfileNotAvailable',) exceptions = ('SiteProfileNotAvailable',)
admin = meta.Admin( admin = meta.Admin(
fields = ( fields = (
(None, {'fields': ('username', 'password_md5')}), (None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}), (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}), (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}), (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
@ -78,13 +78,35 @@ class User(meta.Model):
return full_name.strip() return full_name.strip()
def set_password(self, raw_password): def set_password(self, raw_password):
import md5 import sha, random
self.password_md5 = md5.new(raw_password).hexdigest() algo = 'sha1'
salt = sha.new(str(random.random())).hexdigest()[:5]
hsh = sha.new(salt+raw_password).hexdigest()
self.password = '%s$%s$%s' % (algo, salt, hsh)
def check_password(self, raw_password): def check_password(self, raw_password):
"Returns a boolean of whether the raw_password was correct." """
Returns a boolean of whether the raw_password was correct. Handles
encryption formats behind the scenes.
"""
# Backwards-compatibility check. Older passwords won't include the
# algorithm or salt.
if '$' not in self.password:
import md5 import md5
return self.password_md5 == md5.new(raw_password).hexdigest() is_correct = (self.password == md5.new(raw_password).hexdigest())
if is_correct:
# Convert the password to the new, more secure format.
self.set_password(raw_password)
self.save()
return is_correct
algo, salt, hsh = self.password.split('$')
if algo == 'md5':
import md5
return hsh == md5.new(salt+raw_password).hexdigest()
elif algo == 'sha1':
import sha
return hsh == sha.new(salt+raw_password).hexdigest()
raise ValueError, "Got unknown password algorithm type in password."
def get_group_permissions(self): def get_group_permissions(self):
"Returns a list of permission strings that this user has through his/her groups." "Returns a list of permission strings that this user has through his/her groups."
@ -176,10 +198,9 @@ class User(meta.Model):
def _module_create_user(username, email, password): def _module_create_user(username, email, password):
"Creates and saves a User with the given username, e-mail and password." "Creates and saves a User with the given username, e-mail and password."
import md5
password_md5 = md5.new(password).hexdigest()
now = datetime.datetime.now() now = datetime.datetime.now()
user = User(None, username, '', '', email.strip().lower(), password_md5, False, True, False, now, now) user = User(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
user.set_password(password)
user.save() user.save()
return user return user

View File

@ -44,9 +44,9 @@ Fields
* ``first_name`` -- Optional. 30 characters or fewer. * ``first_name`` -- Optional. 30 characters or fewer.
* ``last_name`` -- Optional. 30 characters or fewer. * ``last_name`` -- Optional. 30 characters or fewer.
* ``email`` -- Optional. E-mail address. * ``email`` -- Optional. E-mail address.
* ``password_md5`` -- Required. An MD5 hash of the password. (Django * ``password`` -- Required. A hash of, and metadata about, the password.
doesn't store the raw password.) Raw passwords can be arbitrarily long (Django doesn't store the raw password.) Raw passwords can be arbitrarily
and can contain any character. long and can contain any character. See the "Passwords" section below.
* ``is_staff`` -- Boolean. Designates whether this user can access the * ``is_staff`` -- Boolean. Designates whether this user can access the
admin site. admin site.
* ``is_active`` -- Boolean. Designates whether this user can log into the * ``is_active`` -- Boolean. Designates whether this user can log into the
@ -167,6 +167,28 @@ Change a password with ``set_password()``::
>>> u.set_password('new password') >>> u.set_password('new password')
>>> u.save() >>> u.save()
Passwords
---------
**This only applies to the Django development version.** Previous versions,
such as Django 0.90, used simple MD5 hashes without password salts.
The ``password`` field of a ``User`` object is a string in this format::
hashtype$salt$hash
That's hashtype, salt and hash, separated by the dollar-sign character.
Hashtype is either ``sha1`` (default) or ``md5``. Salt is a random string
used to salt the raw password to create the hash.
For example::
sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
The ``User.set_password()`` and ``User.check_password()`` functions handle
the setting and checking of these values behind the scenes.
Anonymous users Anonymous users
--------------- ---------------