173 lines
5.5 KiB
Python
173 lines
5.5 KiB
Python
import datetime
|
|
|
|
from django.core import signing
|
|
from django.db import models, transaction
|
|
from django.utils import timezone
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from .. import app_settings as allauth_app_settings
|
|
from . import app_settings, signals
|
|
from .adapter import get_adapter
|
|
from .managers import EmailAddressManager, EmailConfirmationManager
|
|
from .utils import user_email
|
|
|
|
|
|
class EmailAddress(models.Model):
|
|
|
|
user = models.ForeignKey(
|
|
allauth_app_settings.USER_MODEL,
|
|
verbose_name=_("user"),
|
|
on_delete=models.CASCADE,
|
|
)
|
|
email = models.EmailField(
|
|
unique=app_settings.UNIQUE_EMAIL,
|
|
max_length=app_settings.EMAIL_MAX_LENGTH,
|
|
verbose_name=_("e-mail address"),
|
|
)
|
|
verified = models.BooleanField(verbose_name=_("verified"), default=False)
|
|
primary = models.BooleanField(verbose_name=_("primary"), default=False)
|
|
|
|
objects = EmailAddressManager()
|
|
|
|
class Meta:
|
|
verbose_name = _("email address")
|
|
verbose_name_plural = _("email addresses")
|
|
if not app_settings.UNIQUE_EMAIL:
|
|
unique_together = [("user", "email")]
|
|
|
|
def __str__(self):
|
|
return self.email
|
|
|
|
def set_as_primary(self, conditional=False):
|
|
old_primary = EmailAddress.objects.get_primary(self.user)
|
|
if old_primary:
|
|
if conditional:
|
|
return False
|
|
old_primary.primary = False
|
|
old_primary.save()
|
|
self.primary = True
|
|
self.save()
|
|
user_email(self.user, self.email)
|
|
self.user.save()
|
|
return True
|
|
|
|
def send_confirmation(self, request=None, signup=False):
|
|
if app_settings.EMAIL_CONFIRMATION_HMAC:
|
|
confirmation = EmailConfirmationHMAC(self)
|
|
else:
|
|
confirmation = EmailConfirmation.create(self)
|
|
confirmation.send(request, signup=signup)
|
|
return confirmation
|
|
|
|
def change(self, request, new_email, confirm=True):
|
|
"""
|
|
Given a new email address, change self and re-confirm.
|
|
"""
|
|
with transaction.atomic():
|
|
user_email(self.user, new_email)
|
|
self.user.save()
|
|
self.email = new_email
|
|
self.verified = False
|
|
self.save()
|
|
if confirm:
|
|
self.send_confirmation(request)
|
|
|
|
|
|
class EmailConfirmation(models.Model):
|
|
|
|
email_address = models.ForeignKey(
|
|
EmailAddress,
|
|
verbose_name=_("e-mail address"),
|
|
on_delete=models.CASCADE,
|
|
)
|
|
created = models.DateTimeField(verbose_name=_("created"), default=timezone.now)
|
|
sent = models.DateTimeField(verbose_name=_("sent"), null=True)
|
|
key = models.CharField(verbose_name=_("key"), max_length=64, unique=True)
|
|
|
|
objects = EmailConfirmationManager()
|
|
|
|
class Meta:
|
|
verbose_name = _("email confirmation")
|
|
verbose_name_plural = _("email confirmations")
|
|
|
|
def __str__(self):
|
|
return "confirmation for %s" % self.email_address
|
|
|
|
@classmethod
|
|
def create(cls, email_address):
|
|
key = get_adapter().generate_emailconfirmation_key(email_address.email)
|
|
return cls._default_manager.create(email_address=email_address, key=key)
|
|
|
|
def key_expired(self):
|
|
expiration_date = self.sent + datetime.timedelta(
|
|
days=app_settings.EMAIL_CONFIRMATION_EXPIRE_DAYS
|
|
)
|
|
return expiration_date <= timezone.now()
|
|
|
|
key_expired.boolean = True
|
|
|
|
def confirm(self, request):
|
|
if not self.key_expired() and not self.email_address.verified:
|
|
email_address = self.email_address
|
|
get_adapter(request).confirm_email(request, email_address)
|
|
signals.email_confirmed.send(
|
|
sender=self.__class__,
|
|
request=request,
|
|
email_address=email_address,
|
|
)
|
|
return email_address
|
|
|
|
def send(self, request=None, signup=False):
|
|
get_adapter(request).send_confirmation_mail(request, self, signup)
|
|
self.sent = timezone.now()
|
|
self.save()
|
|
signals.email_confirmation_sent.send(
|
|
sender=self.__class__,
|
|
request=request,
|
|
confirmation=self,
|
|
signup=signup,
|
|
)
|
|
|
|
|
|
class EmailConfirmationHMAC:
|
|
def __init__(self, email_address):
|
|
self.email_address = email_address
|
|
|
|
@property
|
|
def key(self):
|
|
return signing.dumps(obj=self.email_address.pk, salt=app_settings.SALT)
|
|
|
|
@classmethod
|
|
def from_key(cls, key):
|
|
try:
|
|
max_age = 60 * 60 * 24 * app_settings.EMAIL_CONFIRMATION_EXPIRE_DAYS
|
|
pk = signing.loads(key, max_age=max_age, salt=app_settings.SALT)
|
|
ret = EmailConfirmationHMAC(EmailAddress.objects.get(pk=pk, verified=False))
|
|
except (
|
|
signing.SignatureExpired,
|
|
signing.BadSignature,
|
|
EmailAddress.DoesNotExist,
|
|
):
|
|
ret = None
|
|
return ret
|
|
|
|
def confirm(self, request):
|
|
if not self.email_address.verified:
|
|
email_address = self.email_address
|
|
get_adapter(request).confirm_email(request, email_address)
|
|
signals.email_confirmed.send(
|
|
sender=self.__class__,
|
|
request=request,
|
|
email_address=email_address,
|
|
)
|
|
return email_address
|
|
|
|
def send(self, request=None, signup=False):
|
|
get_adapter(request).send_confirmation_mail(request, self, signup)
|
|
signals.email_confirmation_sent.send(
|
|
sender=self.__class__,
|
|
request=request,
|
|
confirmation=self,
|
|
signup=signup,
|
|
)
|