Code

Ticket #61: password_field.2.patch

File password_field.2.patch, 8.3 KB (added by SmileyChris, 8 years ago)

Oops, spaces instead of tabs in documentation this time :(

  • 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/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