confirmation: Liberate get_link_for_object from ConfirmationManager.

This commit is contained in:
Rishi Gupta
2017-07-07 19:38:13 -07:00
committed by Tim Abbott
parent 3b97262647
commit 0f4b71b766
10 changed files with 66 additions and 48 deletions

View File

@@ -43,24 +43,24 @@ def get_object_from_key(confirmation_key):
return obj return obj
return False return False
class ConfirmationManager(models.Manager): def create_confirmation_link(obj, host, confirmation_type):
url_pattern_name = 'confirmation.views.confirm' # type: (Union[ContentType, int], str, int) -> str
def get_link_for_object(self, obj, host):
# type: (Union[ContentType, int], str) -> str
key = generate_key() key = generate_key()
self.create(content_object=obj, date_sent=timezone_now(), confirmation_key=key) Confirmation.objects.create(content_object=obj, date_sent=timezone_now(), confirmation_key=key)
return self.get_activation_url(key, host) return confirmation_url(key, host, confirmation_type)
def get_activation_url(self, confirmation_key, host): def confirmation_url(confirmation_key, host, confirmation_type):
# type: (str, str) -> str # type: (str, str, int) -> str
return '%s%s%s' % (settings.EXTERNAL_URI_SCHEME, return '%s%s%s' % (settings.EXTERNAL_URI_SCHEME,
host, host,
reverse(self.url_pattern_name, reverse(_properties[confirmation_type].url_name,
kwargs={'confirmation_key': confirmation_key})) kwargs={'confirmation_key': confirmation_key}))
class ConfirmationManager(models.Manager):
pass
class EmailChangeConfirmationManager(ConfirmationManager): class EmailChangeConfirmationManager(ConfirmationManager):
url_pattern_name = 'zerver.views.user_settings.confirm_email_change' pass
class Confirmation(models.Model): class Confirmation(models.Model):
content_type = models.ForeignKey(ContentType) content_type = models.ForeignKey(ContentType)
@@ -69,6 +69,12 @@ class Confirmation(models.Model):
date_sent = models.DateTimeField('sent') date_sent = models.DateTimeField('sent')
confirmation_key = models.CharField('activation key', max_length=40) confirmation_key = models.CharField('activation key', max_length=40)
USER_REGISTRATION = 1
INVITATION = 2
EMAIL_CHANGE = 3
UNSUBSCRIBE = 4
SERVER_REGISTRATION = 5
objects = ConfirmationManager() objects = ConfirmationManager()
class Meta(object): class Meta(object):
@@ -85,6 +91,17 @@ class EmailChangeConfirmation(Confirmation):
objects = EmailChangeConfirmationManager() objects = EmailChangeConfirmationManager()
class ConfirmationType(object):
def __init__(self, url_name):
# type: (str) -> None
self.url_name = url_name
_properties = {
Confirmation.USER_REGISTRATION: ConfirmationType('confirmation.views.confirm'),
Confirmation.INVITATION: ConfirmationType('confirmation.views.confirm'),
Confirmation.EMAIL_CHANGE: ConfirmationType('zerver.views.user_settings.confirm_email_change'),
}
# Conirmation pathways for which there is no content_object that we need to # Conirmation pathways for which there is no content_object that we need to
# keep track of. # keep track of.

View File

@@ -59,7 +59,7 @@ from django.core.exceptions import ValidationError
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from confirmation.models import Confirmation, EmailChangeConfirmation from confirmation.models import Confirmation, create_confirmation_link
import six import six
from six.moves import filter from six.moves import filter
from six.moves import map from six.moves import map
@@ -641,7 +641,7 @@ def do_start_email_change_process(user_profile, new_email):
obj = EmailChangeStatus.objects.create(new_email=new_email, old_email=old_email, obj = EmailChangeStatus.objects.create(new_email=new_email, old_email=old_email,
user_profile=user_profile, realm=user_profile.realm) user_profile=user_profile, realm=user_profile.realm)
activation_url = EmailChangeConfirmation.objects.get_link_for_object(obj, user_profile.realm.host) activation_url = create_confirmation_link(obj, user_profile.realm.host, Confirmation.EMAIL_CHANGE)
context = {'realm': user_profile.realm, 'old_email': old_email, 'new_email': new_email, context = {'realm': user_profile.realm, 'old_email': old_email, 'new_email': new_email,
'activate_url': activation_url} 'activate_url': activation_url}
send_email('zerver/emails/confirm_new_email', to_email=new_email, send_email('zerver/emails/confirm_new_email', to_email=new_email,
@@ -3102,7 +3102,7 @@ def do_send_confirmation_email(invitee, referrer, body):
`invitee` is a PreregistrationUser. `invitee` is a PreregistrationUser.
`referrer` is a UserProfile. `referrer` is a UserProfile.
""" """
activation_url = Confirmation.objects.get_link_for_object(invitee, referrer.realm.host) activation_url = create_confirmation_link(invitee, referrer.realm.host, Confirmation.INVITATION)
context = {'referrer': referrer, 'custom_body': body, 'activate_url': activation_url, context = {'referrer': referrer, 'custom_body': body, 'activate_url': activation_url,
'referrer_realm_name': referrer.realm.name} 'referrer_realm_name': referrer.realm.name}
from_name = u"%s (via Zulip)" % (referrer.full_name,) from_name = u"%s (via Zulip)" % (referrer.full_name,)

View File

@@ -2,7 +2,7 @@ from __future__ import print_function
from typing import cast, Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple, Text from typing import cast, Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple, Text
from confirmation.models import Confirmation from confirmation.models import Confirmation, create_confirmation_link
from django.conf import settings from django.conf import settings
from django.template import loader from django.template import loader
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
@@ -37,7 +37,8 @@ def unsubscribe_token(user_profile):
# type: (UserProfile) -> Text # type: (UserProfile) -> Text
# Leverage the Django confirmations framework to generate and track unique # Leverage the Django confirmations framework to generate and track unique
# unsubscription tokens. # unsubscription tokens.
return Confirmation.objects.get_link_for_object(user_profile, 'unused').split("/")[-1] # Will be changed to UNSUBSCRIBE in a few commits. This is the current behavior.
return create_confirmation_link(user_profile, 'unused', Confirmation.USER_REGISTRATION).split("/")[-1]
def one_click_unsubscribe_link(user_profile, email_type): def one_click_unsubscribe_link(user_profile, email_type):
# type: (UserProfile, Text) -> Text # type: (UserProfile, Text) -> Text

View File

@@ -5,7 +5,7 @@ from typing import Any
from argparse import ArgumentParser from argparse import ArgumentParser
from django.core.management.base import CommandError from django.core.management.base import CommandError
from confirmation.models import Confirmation from confirmation.models import Confirmation, create_confirmation_link
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
from zerver.models import PreregistrationUser, email_allowed_for_realm from zerver.models import PreregistrationUser, email_allowed_for_realm
@@ -53,4 +53,5 @@ class Command(ZulipBaseCommand):
prereg_user = PreregistrationUser(email=email, realm=realm) prereg_user = PreregistrationUser(email=email, realm=realm)
prereg_user.save() prereg_user.save()
print(email + ": " + Confirmation.objects.get_link_for_object(prereg_user, realm.host)) print(email + ": " + create_confirmation_link(prereg_user, realm.host,
Confirmation.INVITATION))

View File

@@ -38,7 +38,7 @@ from zerver.models import \
get_realm, email_to_username, UserProfile, \ get_realm, email_to_username, UserProfile, \
PreregistrationUser, Realm, get_user PreregistrationUser, Realm, get_user
from confirmation.models import Confirmation from confirmation.models import Confirmation, confirmation_url
from zproject.backends import ZulipDummyBackend, EmailAuthBackend, \ from zproject.backends import ZulipDummyBackend, EmailAuthBackend, \
GoogleMobileOauth2Backend, ZulipRemoteUserBackend, ZulipLDAPAuthBackend, \ GoogleMobileOauth2Backend, ZulipRemoteUserBackend, ZulipLDAPAuthBackend, \
@@ -995,7 +995,7 @@ class GoogleSubdomainLoginTest(GoogleOAuthTest):
self.client_get(result.url) self.client_get(result.url)
assert Confirmation.objects.all().count() == 1 assert Confirmation.objects.all().count() == 1
confirmation = Confirmation.objects.all().first() confirmation = Confirmation.objects.all().first()
url = Confirmation.objects.get_activation_url(confirmation.confirmation_key, realm.host) url = confirmation_url(confirmation.confirmation_key, realm.host, Confirmation.USER_REGISTRATION)
result = self.client_get(url) result = self.client_get(url)
key_match = re.search('value="(?P<key>[0-9a-f]+)" name="key"', result.content.decode("utf-8")) key_match = re.search('value="(?P<key>[0-9a-f]+)" name="key"', result.content.decode("utf-8"))
result = self.client_post('/accounts/register/', result = self.client_post('/accounts/register/',
@@ -1042,7 +1042,8 @@ class GoogleLoginTest(GoogleOAuthTest):
self.client_get(result.url) self.client_get(result.url)
assert Confirmation.objects.all().count() == 1 assert Confirmation.objects.all().count() == 1
confirmation = Confirmation.objects.all().first() confirmation = Confirmation.objects.all().first()
url = Confirmation.objects.get_activation_url(confirmation.confirmation_key, settings.EXTERNAL_HOST) url = confirmation_url(confirmation.confirmation_key,
settings.EXTERNAL_HOST, Confirmation.USER_REGISTRATION)
result = self.client_get(url) result = self.client_get(url)
key_match = re.search('value="(?P<key>[0-9a-f]+)" name="key"', result.content.decode("utf-8")) key_match = re.search('value="(?P<key>[0-9a-f]+)" name="key"', result.content.decode("utf-8"))
result = self.client_post('/accounts/register/', result = self.client_post('/accounts/register/',

View File

@@ -12,7 +12,7 @@ from django.http import HttpResponse
from django.urls import reverse from django.urls import reverse
from django.utils.timezone import now from django.utils.timezone import now
from confirmation.models import EmailChangeConfirmation, generate_key from confirmation.models import Confirmation, generate_key, confirmation_url
from zerver.lib.actions import do_start_email_change_process, do_set_realm_property from zerver.lib.actions import do_start_email_change_process, do_set_realm_property
from zerver.lib.test_classes import ( from zerver.lib.test_classes import (
ZulipTestCase, ZulipTestCase,
@@ -26,8 +26,7 @@ class EmailChangeTestCase(ZulipTestCase):
# type: () -> None # type: () -> None
self.login(self.example_email("hamlet")) self.login(self.example_email("hamlet"))
key = generate_key() key = generate_key()
url = EmailChangeConfirmation.objects.get_activation_url( url = confirmation_url(key, 'testserver', Confirmation.EMAIL_CHANGE)
key, 'testserver')
response = self.client_get(url) response = self.client_get(url)
self.assert_in_success_response(["Whoops"], response) self.assert_in_success_response(["Whoops"], response)
@@ -35,16 +34,14 @@ class EmailChangeTestCase(ZulipTestCase):
# type: () -> None # type: () -> None
self.login(self.example_email("hamlet")) self.login(self.example_email("hamlet"))
key = 'invalid key' key = 'invalid key'
url = EmailChangeConfirmation.objects.get_activation_url( url = confirmation_url(key, 'testserver', Confirmation.EMAIL_CHANGE)
key, 'testserver')
response = self.client_get(url) response = self.client_get(url)
self.assert_in_success_response(["Whoops"], response) self.assert_in_success_response(["Whoops"], response)
def test_email_change_when_not_logging_in(self): def test_email_change_when_not_logging_in(self):
# type: () -> None # type: () -> None
key = generate_key() key = generate_key()
url = EmailChangeConfirmation.objects.get_activation_url( url = confirmation_url(key, 'testserver', Confirmation.EMAIL_CHANGE)
key, 'testserver')
response = self.client_get(url) response = self.client_get(url)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
@@ -60,10 +57,10 @@ class EmailChangeTestCase(ZulipTestCase):
realm=user_profile.realm) realm=user_profile.realm)
key = generate_key() key = generate_key()
date_sent = now() - datetime.timedelta(days=2) date_sent = now() - datetime.timedelta(days=2)
EmailChangeConfirmation.objects.create(content_object=obj, Confirmation.objects.create(content_object=obj,
date_sent=date_sent, date_sent=date_sent,
confirmation_key=key) confirmation_key=key)
url = EmailChangeConfirmation.objects.get_activation_url(key, user_profile.realm.host) url = confirmation_url(key, user_profile.realm.host, Confirmation.EMAIL_CHANGE)
response = self.client_get(url) response = self.client_get(url)
self.assert_in_success_response(["Whoops"], response) self.assert_in_success_response(["Whoops"], response)
@@ -79,10 +76,10 @@ class EmailChangeTestCase(ZulipTestCase):
user_profile=user_profile, user_profile=user_profile,
realm=user_profile.realm) realm=user_profile.realm)
key = generate_key() key = generate_key()
EmailChangeConfirmation.objects.create(content_object=obj, Confirmation.objects.create(content_object=obj,
date_sent=now(), date_sent=now(),
confirmation_key=key) confirmation_key=key)
url = EmailChangeConfirmation.objects.get_activation_url(key, user_profile.realm.host) url = confirmation_url(key, user_profile.realm.host, Confirmation.EMAIL_CHANGE)
response = self.client_get(url) response = self.client_get(url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)

View File

@@ -11,7 +11,7 @@ from django.utils.timezone import now as timezone_now
from mock import patch, MagicMock from mock import patch, MagicMock
from zerver.lib.test_helpers import MockLDAP from zerver.lib.test_helpers import MockLDAP
from confirmation.models import Confirmation from confirmation.models import Confirmation, create_confirmation_link
from zilencer.models import Deployment from zilencer.models import Deployment
@@ -696,7 +696,7 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
data = {"email": invitee, "referrer_email": current_user_email} data = {"email": invitee, "referrer_email": current_user_email}
invitee = get_prereg_user_by_email(data["email"]) invitee = get_prereg_user_by_email(data["email"])
referrer = self.example_user(referrer_user) referrer = self.example_user(referrer_user)
link = Confirmation.objects.get_link_for_object(invitee, referrer.realm.host) link = create_confirmation_link(invitee, referrer.realm.host, Confirmation.INVITATION)
context = common_context(referrer) context = common_context(referrer)
context.update({ context.update({
'activate_url': link, 'activate_url': link,

View File

@@ -19,7 +19,7 @@ from django.core import signing
from six.moves import urllib from six.moves import urllib
from typing import Any, Dict, List, Optional, Tuple, Text from typing import Any, Dict, List, Optional, Tuple, Text
from confirmation.models import Confirmation from confirmation.models import Confirmation, create_confirmation_link
from zerver.context_processors import zulip_default_context from zerver.context_processors import zulip_default_context
from zerver.forms import HomepageForm, OurAuthenticationForm, \ from zerver.forms import HomepageForm, OurAuthenticationForm, \
WRONG_SUBDOMAIN_ERROR WRONG_SUBDOMAIN_ERROR
@@ -61,7 +61,7 @@ def maybe_send_to_registration(request, email, full_name=''):
prereg_user = create_preregistration_user(email, request) prereg_user = create_preregistration_user(email, request)
return redirect("".join(( return redirect("".join((
Confirmation.objects.get_link_for_object(prereg_user, request.get_host()), create_confirmation_link(prereg_user, request.get_host(), Confirmation.USER_REGISTRATION),
'?full_name=', '?full_name=',
# urllib does not handle Unicode, so coerece to encoded byte string # urllib does not handle Unicode, so coerece to encoded byte string
# Explanation: http://stackoverflow.com/a/5605354/90777 # Explanation: http://stackoverflow.com/a/5605354/90777

View File

@@ -33,7 +33,8 @@ from zerver.lib.utils import get_subdomain
from zerver.lib.timezone import get_all_timezones from zerver.lib.timezone import get_all_timezones
from zproject.backends import password_auth_enabled from zproject.backends import password_auth_enabled
from confirmation.models import Confirmation, RealmCreationKey, check_key_is_valid from confirmation.models import Confirmation, RealmCreationKey, check_key_is_valid, \
create_confirmation_link
import logging import logging
import requests import requests
@@ -312,7 +313,7 @@ def send_registration_completion_email(email, request, realm_creation=False):
can complete their registration. can complete their registration.
""" """
prereg_user = create_preregistration_user(email, request, realm_creation) prereg_user = create_preregistration_user(email, request, realm_creation)
activation_url = Confirmation.objects.get_link_for_object(prereg_user, request.get_host()) activation_url = create_confirmation_link(prereg_user, request.get_host(), Confirmation.USER_REGISTRATION)
send_email('zerver/emails/confirm_registration', to_email=email, from_address=FromAddress.NOREPLY, send_email('zerver/emails/confirm_registration', to_email=email, from_address=FromAddress.NOREPLY,
context={'activate_url': activation_url}) context={'activate_url': activation_url})
if settings.DEVELOPMENT and realm_creation: if settings.DEVELOPMENT and realm_creation:

View File

@@ -34,7 +34,7 @@ from zerver.lib.send_email import send_future_email, send_email_from_dict, \
from zerver.lib.email_mirror import process_message as mirror_email from zerver.lib.email_mirror import process_message as mirror_email
from zerver.decorator import JsonableError from zerver.decorator import JsonableError
from zerver.tornado.socket import req_redis_key from zerver.tornado.socket import req_redis_key
from confirmation.models import Confirmation from confirmation.models import Confirmation, create_confirmation_link
from zerver.lib.db import reset_queries from zerver.lib.db import reset_queries
from zerver.lib.redis_utils import get_redis_client from zerver.lib.redis_utils import get_redis_client
from zerver.lib.str_utils import force_str from zerver.lib.str_utils import force_str
@@ -183,7 +183,7 @@ class ConfirmationEmailWorker(QueueProcessingWorker):
do_send_confirmation_email(invitee, referrer, body) do_send_confirmation_email(invitee, referrer, body)
# queue invitation reminder for two days from now. # queue invitation reminder for two days from now.
link = Confirmation.objects.get_link_for_object(invitee, referrer.realm.host) link = create_confirmation_link(invitee, referrer.realm.host, Confirmation.INVITATION)
context = common_context(referrer) context = common_context(referrer)
context.update({ context.update({
'activate_url': link, 'activate_url': link,