2012-09-28 22:29:48 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
# Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com>
|
|
|
|
|
|
|
|
__revision__ = '$Id: models.py 28 2009-10-22 15:03:02Z jarek.zgoda $'
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
|
|
|
from django.db import models
|
|
|
|
from django.core.urlresolvers import reverse
|
|
|
|
from django.core.mail import send_mail
|
|
|
|
from django.conf import settings
|
|
|
|
from django.template import loader, Context
|
|
|
|
from django.contrib.sites.models import Site
|
|
|
|
from django.contrib.contenttypes.models import ContentType
|
2016-10-10 14:52:01 +02:00
|
|
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
2012-09-28 22:29:48 +02:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2017-02-25 21:02:13 +01:00
|
|
|
from django.utils import timezone
|
2012-09-28 22:29:48 +02:00
|
|
|
|
|
|
|
from confirmation.util import get_status_field
|
2013-08-08 16:50:58 +02:00
|
|
|
from zerver.lib.utils import generate_random_token
|
2017-01-20 12:27:38 +01:00
|
|
|
from zerver.models import PreregistrationUser, EmailChangeStatus
|
2017-03-03 19:01:52 +01:00
|
|
|
from typing import Any, Dict, Optional, Text, Union
|
2012-09-28 22:29:48 +02:00
|
|
|
|
2013-02-28 20:07:04 +01:00
|
|
|
B16_RE = re.compile('^[a-f0-9]{40}$')
|
|
|
|
|
2016-06-22 21:16:02 +02:00
|
|
|
def check_key_is_valid(creation_key):
|
2016-11-23 05:21:41 +01:00
|
|
|
# type: (Text) -> bool
|
2016-06-22 21:16:02 +02:00
|
|
|
if not RealmCreationKey.objects.filter(creation_key=creation_key).exists():
|
|
|
|
return False
|
2017-02-25 21:02:13 +01:00
|
|
|
days_sofar = (timezone.now() - RealmCreationKey.objects.get(creation_key=creation_key).date_created).days
|
2016-06-22 21:16:02 +02:00
|
|
|
# Realm creation link expires after settings.REALM_CREATION_LINK_VALIDITY_DAYS
|
|
|
|
if days_sofar <= settings.REALM_CREATION_LINK_VALIDITY_DAYS:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2013-02-28 20:07:04 +01:00
|
|
|
def generate_key():
|
2016-11-23 05:21:41 +01:00
|
|
|
# type: () -> Text
|
2013-08-08 16:50:58 +02:00
|
|
|
return generate_random_token(40)
|
2013-02-28 20:07:04 +01:00
|
|
|
|
2016-08-12 23:19:34 +02:00
|
|
|
def generate_activation_url(key, host=None):
|
2016-11-23 05:21:41 +01:00
|
|
|
# type: (Text, Optional[str]) -> Text
|
2016-08-12 23:19:34 +02:00
|
|
|
if host is None:
|
|
|
|
host = settings.EXTERNAL_HOST
|
2015-08-19 04:51:48 +02:00
|
|
|
return u'%s%s%s' % (settings.EXTERNAL_URI_SCHEME,
|
2016-08-12 23:19:34 +02:00
|
|
|
host,
|
2015-08-19 04:51:48 +02:00
|
|
|
reverse('confirmation.views.confirm',
|
|
|
|
kwargs={'confirmation_key': key}))
|
2012-09-28 22:29:48 +02:00
|
|
|
|
2016-06-22 21:16:02 +02:00
|
|
|
def generate_realm_creation_url():
|
2016-11-23 05:21:41 +01:00
|
|
|
# type: () -> Text
|
2016-06-22 21:16:02 +02:00
|
|
|
key = generate_key()
|
2017-02-25 21:02:13 +01:00
|
|
|
RealmCreationKey.objects.create(creation_key=key, date_created=timezone.now())
|
2016-06-22 21:16:02 +02:00
|
|
|
return u'%s%s%s' % (settings.EXTERNAL_URI_SCHEME,
|
|
|
|
settings.EXTERNAL_HOST,
|
|
|
|
reverse('zerver.views.create_realm',
|
2016-12-03 00:04:17 +01:00
|
|
|
kwargs={'creation_key': key}))
|
2012-09-28 22:29:48 +02:00
|
|
|
|
|
|
|
class ConfirmationManager(models.Manager):
|
|
|
|
|
|
|
|
def confirm(self, confirmation_key):
|
2017-01-20 12:27:38 +01:00
|
|
|
# type: (str) -> Union[bool, PreregistrationUser, EmailChangeStatus]
|
2013-02-28 20:07:04 +01:00
|
|
|
if B16_RE.search(confirmation_key):
|
2012-09-28 22:29:48 +02:00
|
|
|
try:
|
|
|
|
confirmation = self.get(confirmation_key=confirmation_key)
|
|
|
|
except self.model.DoesNotExist:
|
|
|
|
return False
|
2017-01-20 10:09:35 +01:00
|
|
|
|
|
|
|
max_days = self.get_link_validity_in_days()
|
2017-02-25 21:02:13 +01:00
|
|
|
time_elapsed = timezone.now() - confirmation.date_sent
|
2017-01-20 10:09:35 +01:00
|
|
|
if time_elapsed.total_seconds() > max_days * 24 * 3600:
|
|
|
|
return False
|
|
|
|
|
2012-09-28 22:35:05 +02:00
|
|
|
obj = confirmation.content_object
|
2015-08-19 20:52:50 +02:00
|
|
|
status_field = get_status_field(obj._meta.app_label, obj._meta.model_name)
|
2012-09-28 22:35:05 +02:00
|
|
|
setattr(obj, status_field, getattr(settings, 'STATUS_ACTIVE', 1))
|
|
|
|
obj.save()
|
|
|
|
return obj
|
2012-09-28 22:29:48 +02:00
|
|
|
return False
|
|
|
|
|
2016-08-12 23:19:34 +02:00
|
|
|
def get_link_for_object(self, obj, host=None):
|
2016-11-23 05:21:41 +01:00
|
|
|
# type: (Union[ContentType, int], Optional[str]) -> Text
|
2013-02-28 20:07:54 +01:00
|
|
|
key = generate_key()
|
2017-02-25 21:02:13 +01:00
|
|
|
self.create(content_object=obj, date_sent=timezone.now(), confirmation_key=key)
|
2017-01-17 11:11:51 +01:00
|
|
|
return self.get_activation_url(key, host=host)
|
|
|
|
|
|
|
|
def get_activation_url(self, confirmation_key, host=None):
|
|
|
|
# type: (Text, Optional[str]) -> Text
|
|
|
|
return generate_activation_url(confirmation_key, host=host)
|
2013-02-28 20:07:54 +01:00
|
|
|
|
2017-01-20 10:09:35 +01:00
|
|
|
def get_link_validity_in_days(self):
|
|
|
|
# type: () -> int
|
|
|
|
return getattr(settings, 'EMAIL_CONFIRMATION_DAYS', 10)
|
|
|
|
|
2013-01-03 17:01:03 +01:00
|
|
|
def send_confirmation(self, obj, email_address, additional_context=None,
|
2017-01-08 17:28:33 +01:00
|
|
|
subject_template_path=None, body_template_path=None, html_body_template_path=None,
|
2017-02-12 21:21:31 +01:00
|
|
|
host=None, custom_body=None):
|
|
|
|
# type: (ContentType, Text, Optional[Dict[str, Any]], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> Confirmation
|
2013-02-28 20:07:04 +01:00
|
|
|
confirmation_key = generate_key()
|
2012-09-28 22:29:48 +02:00
|
|
|
current_site = Site.objects.get_current()
|
2017-01-17 11:11:51 +01:00
|
|
|
activate_url = self.get_activation_url(confirmation_key, host=host)
|
2012-09-28 22:29:48 +02:00
|
|
|
context = Context({
|
|
|
|
'activate_url': activate_url,
|
|
|
|
'current_site': current_site,
|
|
|
|
'confirmation_key': confirmation_key,
|
|
|
|
'target': obj,
|
|
|
|
'days': getattr(settings, 'EMAIL_CONFIRMATION_DAYS', 10),
|
2017-02-12 21:21:31 +01:00
|
|
|
'custom_body': custom_body,
|
2012-09-28 22:29:48 +02:00
|
|
|
})
|
2013-01-03 17:01:03 +01:00
|
|
|
if additional_context is not None:
|
|
|
|
context.update(additional_context)
|
2016-07-27 02:56:36 +02:00
|
|
|
if obj.realm is not None and obj.realm.is_zephyr_mirror_realm:
|
|
|
|
template_name = "mituser"
|
|
|
|
else:
|
|
|
|
template_name = obj._meta.model_name
|
2012-09-28 22:29:48 +02:00
|
|
|
templates = [
|
2017-01-08 17:28:33 +01:00
|
|
|
'confirmation/%s_confirmation_email.subject' % (template_name,),
|
|
|
|
'confirmation/confirmation_email.subject',
|
2012-09-28 22:29:48 +02:00
|
|
|
]
|
2013-01-03 17:01:03 +01:00
|
|
|
if subject_template_path:
|
|
|
|
template = loader.get_template(subject_template_path)
|
|
|
|
else:
|
|
|
|
template = loader.select_template(templates)
|
2012-09-28 22:29:48 +02:00
|
|
|
subject = template.render(context).strip().replace(u'\n', u' ') # no newlines, please
|
|
|
|
templates = [
|
2017-01-08 17:28:33 +01:00
|
|
|
'confirmation/%s_confirmation_email.txt' % (template_name,),
|
|
|
|
'confirmation/confirmation_email.txt',
|
2012-09-28 22:29:48 +02:00
|
|
|
]
|
2013-01-03 17:01:03 +01:00
|
|
|
if body_template_path:
|
|
|
|
template = loader.get_template(body_template_path)
|
|
|
|
else:
|
|
|
|
template = loader.select_template(templates)
|
2017-01-08 17:28:33 +01:00
|
|
|
if html_body_template_path:
|
|
|
|
html_template = loader.get_template(html_body_template_path)
|
|
|
|
else:
|
|
|
|
html_template = loader.get_template('confirmation/%s_confirmation_email.html' % (template_name,))
|
2012-09-28 22:29:48 +02:00
|
|
|
body = template.render(context)
|
2017-01-08 17:28:33 +01:00
|
|
|
if html_template:
|
|
|
|
html_content = html_template.render(context)
|
|
|
|
send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [email_address], html_message=html_content)
|
2017-02-25 21:02:13 +01:00
|
|
|
return self.create(content_object=obj, date_sent=timezone.now(), confirmation_key=confirmation_key)
|
2012-09-28 22:29:48 +02:00
|
|
|
|
2017-01-20 12:27:38 +01:00
|
|
|
class EmailChangeConfirmationManager(ConfirmationManager):
|
|
|
|
def get_activation_url(self, key, host=None):
|
|
|
|
# type: (Text, Optional[str]) -> Text
|
|
|
|
if host is None:
|
|
|
|
# This will raise exception if the key doesn't exist.
|
|
|
|
host = self.get(confirmation_key=key).content_object.realm.host
|
|
|
|
return u'%s%s%s' % (settings.EXTERNAL_URI_SCHEME,
|
|
|
|
host,
|
|
|
|
reverse('zerver.views.user_settings.confirm_email_change',
|
|
|
|
kwargs={'confirmation_key': key}))
|
|
|
|
|
|
|
|
def get_link_validity_in_days(self):
|
|
|
|
# type: () -> int
|
|
|
|
return settings.EMAIL_CHANGE_CONFIRMATION_DAYS
|
2012-09-28 22:29:48 +02:00
|
|
|
|
|
|
|
class Confirmation(models.Model):
|
|
|
|
content_type = models.ForeignKey(ContentType)
|
|
|
|
object_id = models.PositiveIntegerField()
|
2016-10-10 14:52:01 +02:00
|
|
|
content_object = GenericForeignKey('content_type', 'object_id')
|
2012-09-28 22:29:48 +02:00
|
|
|
date_sent = models.DateTimeField(_('sent'))
|
|
|
|
confirmation_key = models.CharField(_('activation key'), max_length=40)
|
|
|
|
|
|
|
|
objects = ConfirmationManager()
|
|
|
|
|
2015-10-14 22:43:04 +02:00
|
|
|
class Meta(object):
|
2012-09-28 22:29:48 +02:00
|
|
|
verbose_name = _('confirmation email')
|
|
|
|
verbose_name_plural = _('confirmation emails')
|
|
|
|
|
|
|
|
def __unicode__(self):
|
2016-11-23 05:21:41 +01:00
|
|
|
# type: () -> Text
|
2016-06-20 17:19:54 +02:00
|
|
|
return _('confirmation email for %s') % (self.content_object,)
|
2016-06-22 21:16:02 +02:00
|
|
|
|
2017-01-20 12:27:38 +01:00
|
|
|
class EmailChangeConfirmation(Confirmation):
|
|
|
|
class Meta(object):
|
|
|
|
proxy = True
|
|
|
|
|
|
|
|
objects = EmailChangeConfirmationManager()
|
|
|
|
|
2016-06-22 21:16:02 +02:00
|
|
|
class RealmCreationKey(models.Model):
|
|
|
|
creation_key = models.CharField(_('activation key'), max_length=40)
|
2017-02-25 21:02:13 +01:00
|
|
|
date_created = models.DateTimeField(_('created'), default=timezone.now)
|