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