1from plain import models
2
3from . import validators
4from .hashers import (
5 hash_password,
6 identify_hasher,
7)
8
9
10class PasswordField(models.CharField):
11 def __init__(self, *args, **kwargs):
12 kwargs["max_length"] = 128
13 kwargs.setdefault(
14 "validators",
15 [
16 validators.MinimumLengthValidator(),
17 validators.CommonPasswordValidator(),
18 validators.NumericPasswordValidator(),
19 ],
20 )
21 super().__init__(*args, **kwargs)
22
23 def deconstruct(self):
24 name, path, args, kwargs = super().deconstruct()
25 if kwargs.get("max_length") == 128:
26 del kwargs["max_length"]
27 return name, path, args, kwargs
28
29 def pre_save(self, model_instance, add):
30 value = super().pre_save(model_instance, add)
31
32 if value and not self._is_hashed(value):
33 value = hash_password(value)
34 # Set the hashed value back on the instance immediately too
35 setattr(model_instance, self.attname, value)
36
37 return value
38
39 @staticmethod
40 def _is_hashed(value):
41 try:
42 identify_hasher(value)
43 return True
44 except ValueError:
45 return False