from datetime import datetime, time from django.core.mail import send_mail from django.db import models from django.utils.translation import gettext_lazy as _ from django.utils.http import base36_to_int, int_to_base36 from django.conf import settings from django.utils.crypto import constant_time_compare, salted_hmac from .validators import ASCIIUsernameValidator class User(models.Model): username = models.CharField( _('username'), max_length=25, unique=True, help_text=_('Required. 25 characters or fewer. Letters, digits and _ only.'), validators=[ASCIIUsernameValidator()], error_messages={ 'unique': _("A _user with that username already exists."), }, ) password = models.CharField(_('password'), max_length=128) last_login = models.DateTimeField(_('last login'), blank=True, null=True) email = models.EmailField(_('email address'), unique=True) class Meta: db_table = '_user' verbose_name = verbose_name_plural = '用户信息表' def set_password(self, password): # TODO: 密码强度检验,密码hash存储 self.password = password def send_email(self, subject, message, from_email=None, **kwargs): send_mail(subject, message, from_email, [self.email], **kwargs) def make_token(self): return self._make_token(_timestamp()) def check_token(self, token): if not token: return False try: ts_b36, hash_str = token.split('-') except ValueError: return False try: ts = base36_to_int(ts_b36) except ValueError: return False if self._make_token(ts) != token: return False timestamp = _timestamp() if (timestamp - ts) > settings.PASSWORD_RESET_TIMEOUT: return False return True def _make_token(self, timestamp): ts_b36 = int_to_base36(timestamp) salt = settings.SALT value = self._make_hash_value(timestamp) secret = settings.SECRET_KEY hash_str = salted_hmac( salt, value, secret=secret, algorithm='sha256' ).hexdigest()[::2] token = "%s-%s" % (ts_b36, hash_str) return token def _make_hash_value(self, timestamp): return f'{self.pk}{self.password}{timestamp}{self.email}' def _timestamp(): dt = datetime.now() return int((dt - datetime(2001, 1, 1)).total_seconds()) class LoginToken(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tokens') token = models.CharField(max_length=256) from folder.models import Folder # 引入内置信号 from django.db.models.signals import post_save # 引入信号接收器的装饰器 from django.dispatch import receiver class Profile(models.Model): # 对应django自带的user user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') # 对应的根目录 root_folder = models.ForeignKey(Folder, null=True, on_delete=models.DO_NOTHING, related_name='profile') def __str__(self): return '_user {}'.format(self.user.username) # 信号接收函数,每当新建User实例的时候自动调用 @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): # pass if created: Profile.objects.create(user=instance) # 信号接收函数,每当更新User实例的时候自动调用 @receiver(post_save, sender=User) def save_user_profile(sender, instance, **kwargs): pass # instance.profile.save() # 信号接收函数,每当新建Profile实例的时候自动调用 @receiver(post_save, sender=Profile) def create_root_folder(sender, instance, created, **kwargs): # pass if created: root_folder = Folder.objects.create() instance.root_folder = root_folder instance.save()