Ticket #61: password_field.3.patch

File password_field.3.patch, 11.2 KB (added by Chris Beaven, 18 years ago)

All db.backends creation data types updated to work with PasswordField

  • django/contrib/admin/templates/widget/password.html

     
     1{% load i18n %}
     2<p>
     3   {{ bound_field.form_fields.0 }} <span class="help">{% trans "Enter new password (or leave blank to keep existing password)." %}</span><br />
     4   {{ bound_field.form_fields.1 }} <span class="help">{% trans "Confirm new password." %}</span>
     5</p>
     6<p>
     7   {{ bound_field.form_fields.2 }}
     8</p>
     9 No newline at end of file
  • django/contrib/auth/models.py

     
    1919        return hsh == sha.new(salt+raw_password).hexdigest()
    2020    raise ValueError, "Got unknown password algorithm type in password."
    2121
     22def encrypt_password(raw_password):
     23    import sha, random
     24    algo = 'sha1'
     25    salt = sha.new(str(random.random())).hexdigest()[:5]
     26    hsh = sha.new(salt+raw_password).hexdigest()
     27    return '%s$%s$%s' % (algo, salt, hsh)
     28
    2229class SiteProfileNotAvailable(Exception):
    2330    pass
    2431
     
    9198    first_name = models.CharField(_('first name'), maxlength=30, blank=True)
    9299    last_name = models.CharField(_('last name'), maxlength=30, blank=True)
    93100    email = models.EmailField(_('e-mail address'), blank=True)
    94     password = models.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]'"))
     101    password = models.PasswordField(_('password'), maxlength=128, show_raw=True, encrypt_func=encrypt_password, help_text=_("Above is the encrypted password. Use '[algo]$[salt]$[hexdigest]'."))
    95102    is_staff = models.BooleanField(_('staff status'), help_text=_("Designates whether the user can log into this admin site."))
    96103    is_active = models.BooleanField(_('active'), default=True, help_text=_("Designates whether this user can log into the Django admin. Unselect this instead of deleting accounts."))
    97104    is_superuser = models.BooleanField(_('superuser status'), help_text=_("Designates that this user has all permissions without explicitly assigning them."))
     
    138145        return full_name.strip()
    139146
    140147    def set_password(self, raw_password):
    141         import sha, random
    142         algo = 'sha1'
    143         salt = sha.new(str(random.random())).hexdigest()[:5]
    144         hsh = sha.new(salt+raw_password).hexdigest()
    145         self.password = '%s$%s$%s' % (algo, salt, hsh)
     148        self.password = encrypt_pasword(raw_password)
    146149
    147150    def check_password(self, raw_password):
    148151        """
  • django/db/backends/ado_mssql/creation.py

     
    1414    'ManyToManyField':   None,
    1515    'NullBooleanField':  'bit',
    1616    'OneToOneField':     'int',
     17    'PasswordField':     'varchar(%(maxlength)s)',
    1718    'PhoneNumberField':  'varchar(20)',
    1819    'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(column)s] CHECK ([%(column)s] > 0)',
    1920    'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)',
  • django/db/backends/mysql/creation.py

     
    1818    'ManyToManyField':   None,
    1919    'NullBooleanField':  'bool',
    2020    'OneToOneField':     'integer',
     21    'PasswordField':     'varchar(%(maxlength)s)',
    2122    'PhoneNumberField':  'varchar(20)',
    2223    'PositiveIntegerField': 'integer UNSIGNED',
    2324    'PositiveSmallIntegerField': 'smallint UNSIGNED',
  • django/db/backends/oracle/creation.py

     
    1414    'ManyToManyField':   None,
    1515    'NullBooleanField':  'integer',
    1616    'OneToOneField':     'integer',
     17    'PasswordField':     'varchar(%(maxlength)s)',
    1718    'PhoneNumberField':  'varchar(20)',
    1819    'PositiveIntegerField': 'integer',
    1920    'PositiveSmallIntegerField': 'smallint',
  • django/db/backends/postgresql/creation.py

     
    1818    'ManyToManyField':   None,
    1919    'NullBooleanField':  'boolean',
    2020    'OneToOneField':     'integer',
     21    'PasswordField':     'varchar(%(maxlength)s)',
    2122    'PhoneNumberField':  'varchar(20)',
    2223    'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
    2324    'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
  • django/db/backends/sqlite3/creation.py

     
    1717    'ManyToManyField':              None,
    1818    'NullBooleanField':             'bool',
    1919    'OneToOneField':                'integer',
     20    'PasswordField':                'varchar(%(maxlength)s)',
    2021    'PhoneNumberField':             'varchar(20)',
    2122    'PositiveIntegerField':         'integer unsigned',
    2223    'PositiveSmallIntegerField':    'smallint unsigned',
  • django/db/models/fields/__init__.py

     
    385385                raise validators.ValidationError, gettext_lazy("This field cannot be null.")
    386386        return str(value)
    387387
     388class PasswordField(CharField):
     389    def __init__(self, *args, **kwargs):
     390        self.show_raw = kwargs.pop('show_raw', False)
     391        self.encrypt_func = kwargs.pop('encrypt_func', None)
     392        if not self.show_raw:
     393            # Since raw will be a hidden field in this case, we can't use maxlength.
     394            # The other solution is adding maxlength=None to HiddenField.__init__ in django.forms.
     395            kwargs.pop('maxlength')
     396        super(PasswordField, self).__init__(*args, **kwargs)
     397
     398    def get_manipulator_field_objs(self):
     399        return [forms.PasswordField,
     400                forms.PasswordField,
     401                self.show_raw and forms.TextField or forms.HiddenField]
     402
     403    def get_manipulator_field_names(self, name_prefix):
     404        return [name_prefix + self.name,
     405                name_prefix + self.name + '_confirm',
     406                name_prefix + self.name + '_raw']
     407
     408    def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
     409        field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
     410        if not self.blank:
     411            # TODO: Making this work in a related context is over my head.
     412            v = validators.AlwaysMatchesOtherField(field_list[1].field_name, gettext_lazy('New password and confirm password fields must match.'))
     413            field_list[0].validator_list.append(v)
     414            field_list[0].is_required = field_list[1].is_required = False
     415            #field_list[0].maxlength = field_list[1].maxlength = self.maxlength
     416            field_list[0].length = field_list[1].length = 10
     417            field_list[2].length = 60
     418        return field_list
     419
     420    def get_manipulator_new_data(self, new_data, rel=False):
     421        change, confirm, raw = self.get_manipulator_field_names('')
     422        if rel:
     423            # TODO: I don't know if this rel stuff is right.
     424            change = new_data.get(change, [None])[0]
     425            confirm = new_data.get(confirm, [None])[0]
     426            raw = new_data.get(raw, [None])[0]
     427        else:
     428            change = new_data.get(change, None)
     429            confirm = new_data.get(confirm, None)
     430            raw = new_data.get(raw, None)
     431        # The change == confirm test should be done by the validator, but
     432        # it can't hurt to check again.
     433        if change and change == confirm:
     434            return self.encrypt(change)
     435        if raw is not None:
     436            return raw
     437        return self.get_default()
     438
     439    def flatten_data(self,follow, obj = None):
     440        val = self._get_val_from_obj(obj)
     441        change, confirm, raw = self.get_manipulator_field_names('')
     442        return {raw: val,
     443                change: '',
     444                confirm: ''}
     445   
     446    def encrypt(self, raw_password):
     447        """
     448        Attempt to encrypt password using the encrypt_func function.
     449        The function should take only one argument: the raw password.
     450        """
     451        if callable(self.encrypt_func):
     452            try:
     453                return self.encrypt_func(raw_password)
     454            except TypeError:  # Most likely the function received incorrect parameters.
     455                pass
     456        return raw_password
     457
    388458# TODO: Maybe move this into contrib, because it's specialized.
    389459class CommaSeparatedIntegerField(CharField):
    390460    def get_manipulator_field_objs(self):
  • docs/model-api.txt

     
    322322
    323323The admin represents this as a ``<select>`` box with "Unknown", "Yes" and "No" choices.
    324324
     325``PasswordField``
     326~~~~~~~~~~~~~~~~~ 
     327
     328A ``PasswordField`` is similar to ``TextField`` but the characters that are
     329entered are masked, typically by asterisks (*), when entered into a form. Note
     330that though the data is masked on entry, it is sent as clear text to the
     331server and stored as plain text in the database. Additional measures (such as
     332using HTTPS) are needed to ensure the security of data sent from a form.
     333
     334The user is required to enter the same password in a password confirmation box
     335to ensure the password was correctly entered.
     336
     337Has a two optional arguments:
     338
     339    ======================  ===================================================
     340    Argument                Description
     341    ======================  ===================================================
     342    ``show_raw``            Either ``True`` or ``False``. Shows the existing
     343                            password value as stored in the database in a text
     344                            input box. Defaults to ``False``, which passes the
     345                            existing password as a hidden input value.
     346
     347    ``encrypt_func``        An optional function to encrypt a new passwords.
     348                            The function should take one argument (the raw
     349                            password) and return the encrypted password.
     350                            This happens on the server when a new password is
     351                            entered.
     352    ======================  ===================================================
     353
     354If ``show_raw`` is set to ``True``, the user can enter a password in to the raw
     355text input box without it being processed by ``encrypt_func``.
     356
    325357``PhoneNumberField``
    326358~~~~~~~~~~~~~~~~~~~~
    327359
Back to Top