2018-05-27 00:17:08 +02:00
|
|
|
import datetime
|
2020-06-11 00:54:34 +02:00
|
|
|
from unittest import mock
|
|
|
|
|
2020-10-27 01:04:07 +01:00
|
|
|
import pytz
|
2017-01-30 23:19:38 +01:00
|
|
|
from django.conf import settings
|
|
|
|
from django.core import mail
|
2018-08-24 07:28:51 +02:00
|
|
|
from django.test import override_settings
|
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.actions import do_change_notification_settings, notify_new_user
|
2017-06-15 20:54:14 +02:00
|
|
|
from zerver.lib.initial_password import initial_password
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
|
|
|
from zerver.models import Realm, Recipient, Stream
|
|
|
|
from zerver.signals import JUST_CREATED_THRESHOLD, get_device_browser, get_device_os
|
|
|
|
|
2017-01-30 23:19:38 +01:00
|
|
|
|
|
|
|
class SendLoginEmailTest(ZulipTestCase):
|
|
|
|
"""
|
|
|
|
Uses django's user_logged_in signal to send emails on new login.
|
|
|
|
|
|
|
|
The receiver handler for this signal is always registered in production,
|
|
|
|
development and testing, but emails are only sent based on SEND_LOGIN_EMAILS setting.
|
|
|
|
|
|
|
|
SEND_LOGIN_EMAILS is set to true in default settings.
|
|
|
|
It is turned off during testing.
|
|
|
|
"""
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_send_login_emails_if_send_login_email_setting_is_true(self) -> None:
|
2017-01-30 23:19:38 +01:00
|
|
|
with self.settings(SEND_LOGIN_EMAILS=True):
|
|
|
|
self.assertTrue(settings.SEND_LOGIN_EMAILS)
|
2017-06-15 20:54:14 +02:00
|
|
|
# we don't use the self.login method since we spoof the user-agent
|
2020-10-27 01:04:07 +01:00
|
|
|
mock_time = datetime.datetime(year=2018, month=1, day=1, tzinfo=datetime.timezone.utc)
|
2018-08-10 00:58:44 +02:00
|
|
|
|
2018-05-27 00:17:08 +02:00
|
|
|
user = self.example_user('hamlet')
|
|
|
|
user.timezone = 'US/Pacific'
|
2018-08-13 23:00:51 +02:00
|
|
|
user.twenty_four_hour_time = False
|
2018-08-10 00:58:44 +02:00
|
|
|
user.date_joined = mock_time - datetime.timedelta(seconds=JUST_CREATED_THRESHOLD + 1)
|
2018-05-27 00:17:08 +02:00
|
|
|
user.save()
|
2020-03-12 14:17:25 +01:00
|
|
|
password = initial_password(user.delivery_email)
|
|
|
|
login_info = dict(
|
|
|
|
username=user.delivery_email,
|
|
|
|
password=password,
|
|
|
|
)
|
2017-06-15 20:54:14 +02:00
|
|
|
firefox_windows = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
|
2020-10-27 01:04:07 +01:00
|
|
|
user_tz = pytz.timezone(user.timezone)
|
|
|
|
mock_time = datetime.datetime(year=2018, month=1, day=1, tzinfo=datetime.timezone.utc)
|
2018-08-04 11:05:12 +02:00
|
|
|
reference_time = mock_time.astimezone(user_tz).strftime('%A, %B %d, %Y at %I:%M%p %Z')
|
2018-05-27 00:17:08 +02:00
|
|
|
with mock.patch('zerver.signals.timezone_now', return_value=mock_time):
|
2020-03-12 14:17:25 +01:00
|
|
|
self.client_post("/accounts/login/",
|
|
|
|
info=login_info,
|
2018-05-27 00:17:08 +02:00
|
|
|
HTTP_USER_AGENT=firefox_windows)
|
2017-01-30 23:19:38 +01:00
|
|
|
|
|
|
|
# email is sent and correct subject
|
|
|
|
self.assertEqual(len(mail.outbox), 1)
|
2017-06-15 20:54:14 +02:00
|
|
|
subject = 'New login from Firefox on Windows'
|
|
|
|
self.assertEqual(mail.outbox[0].subject, subject)
|
2018-05-27 00:17:08 +02:00
|
|
|
# local time is correct and in email's body
|
|
|
|
self.assertIn(reference_time, mail.outbox[0].body)
|
2017-01-30 23:19:38 +01:00
|
|
|
|
2018-08-13 23:00:51 +02:00
|
|
|
# Try again with the 24h time format setting enabled for this user
|
|
|
|
self.logout() # We just logged in, we'd be redirected without this
|
|
|
|
user.twenty_four_hour_time = True
|
|
|
|
user.save()
|
|
|
|
with mock.patch('zerver.signals.timezone_now', return_value=mock_time):
|
2020-03-12 14:17:25 +01:00
|
|
|
self.client_post("/accounts/login/",
|
|
|
|
info=login_info,
|
2018-08-13 23:00:51 +02:00
|
|
|
HTTP_USER_AGENT=firefox_windows)
|
|
|
|
|
|
|
|
reference_time = mock_time.astimezone(user_tz).strftime('%A, %B %d, %Y at %H:%M %Z')
|
|
|
|
self.assertIn(reference_time, mail.outbox[1].body)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_dont_send_login_emails_if_send_login_emails_is_false(self) -> None:
|
2017-01-30 23:19:38 +01:00
|
|
|
self.assertFalse(settings.SEND_LOGIN_EMAILS)
|
2020-03-06 18:40:46 +01:00
|
|
|
user = self.example_user('hamlet')
|
|
|
|
self.login_user(user)
|
2017-01-30 23:19:38 +01:00
|
|
|
|
|
|
|
self.assertEqual(len(mail.outbox), 0)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_dont_send_login_emails_for_new_user_registration_logins(self) -> None:
|
2017-01-30 23:19:38 +01:00
|
|
|
with self.settings(SEND_LOGIN_EMAILS=True):
|
|
|
|
self.register("test@zulip.com", "test")
|
|
|
|
|
2017-08-23 01:14:45 +02:00
|
|
|
# Verify that there's just 1 email for new user registration.
|
|
|
|
self.assertEqual(mail.outbox[0].subject, "Activate your Zulip account")
|
|
|
|
self.assertEqual(len(mail.outbox), 1)
|
2017-01-30 23:19:38 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_without_path_info_dont_send_login_emails_for_new_user_registration_logins(self) -> None:
|
2017-01-30 23:19:38 +01:00
|
|
|
with self.settings(SEND_LOGIN_EMAILS=True):
|
|
|
|
self.client_post('/accounts/home/', {'email': "orange@zulip.com"})
|
|
|
|
self.submit_reg_form_for_user("orange@zulip.com", "orange", PATH_INFO='')
|
|
|
|
|
|
|
|
for email in mail.outbox:
|
2017-06-15 20:54:14 +02:00
|
|
|
subject = 'New login from an unknown browser on an unknown operating system'
|
|
|
|
self.assertNotEqual(email.subject, subject)
|
2017-01-30 23:19:38 +01:00
|
|
|
|
2018-08-24 07:28:51 +02:00
|
|
|
@override_settings(SEND_LOGIN_EMAILS=True)
|
|
|
|
def test_enable_login_emails_user_setting(self) -> None:
|
|
|
|
user = self.example_user('hamlet')
|
2020-10-27 01:04:07 +01:00
|
|
|
mock_time = datetime.datetime(year=2018, month=1, day=1, tzinfo=datetime.timezone.utc)
|
2018-08-24 07:28:51 +02:00
|
|
|
|
|
|
|
user.timezone = 'US/Pacific'
|
|
|
|
user.date_joined = mock_time - datetime.timedelta(seconds=JUST_CREATED_THRESHOLD + 1)
|
|
|
|
user.save()
|
|
|
|
|
|
|
|
do_change_notification_settings(user, "enable_login_emails", False)
|
|
|
|
self.assertFalse(user.enable_login_emails)
|
|
|
|
with mock.patch('zerver.signals.timezone_now', return_value=mock_time):
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2018-08-24 07:28:51 +02:00
|
|
|
self.assertEqual(len(mail.outbox), 0)
|
|
|
|
|
|
|
|
do_change_notification_settings(user, "enable_login_emails", True)
|
|
|
|
self.assertTrue(user.enable_login_emails)
|
|
|
|
with mock.patch('zerver.signals.timezone_now', return_value=mock_time):
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2018-08-24 07:28:51 +02:00
|
|
|
self.assertEqual(len(mail.outbox), 1)
|
|
|
|
|
|
|
|
|
2017-01-30 23:19:38 +01:00
|
|
|
class TestBrowserAndOsUserAgentStrings(ZulipTestCase):
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def setUp(self) -> None:
|
2019-10-19 20:47:00 +02:00
|
|
|
super().setUp()
|
2017-01-30 23:19:38 +01:00
|
|
|
self.user_agents = [
|
|
|
|
('mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' +
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
'Chrome/54.0.2840.59 Safari/537.36', 'Chrome', 'Linux'),
|
2017-01-30 23:19:38 +01:00
|
|
|
('mozilla/5.0 (windows nt 6.1; win64; x64) applewebkit/537.36 (khtml, like gecko) ' +
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
'chrome/56.0.2924.87 safari/537.36', 'Chrome', 'Windows'),
|
2021-02-12 03:52:14 +01:00
|
|
|
('mozilla/5.0 (windows nt 6.1; wow64; rv:51.0) gecko/20100101 firefox/51.0', 'Firefox', 'Windows'),
|
|
|
|
('mozilla/5.0 (windows nt 6.1; wow64; trident/7.0; rv:11.0) like gecko', 'Internet Explorer', 'Windows'),
|
|
|
|
('Mozilla/5.0 (Android; Mobile; rv:27.0) Gecko/27.0 Firefox/27.0', 'Firefox', 'Android'),
|
2017-11-19 11:08:43 +01:00
|
|
|
('Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) '
|
|
|
|
'AppleWebKit/602.1.50 (KHTML, like Gecko) '
|
|
|
|
'CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1', 'Chrome', 'iOS'),
|
2017-01-30 23:19:38 +01:00
|
|
|
('Mozilla/5.0 (iPad; CPU OS 6_1_3 like Mac OS X) ' +
|
|
|
|
'AppleWebKit/536.26 (KHTML, like Gecko) ' +
|
|
|
|
'Version/6.0 Mobile/10B329 Safari/8536.25', 'Safari', 'iOS'),
|
|
|
|
('Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_4 like Mac OS X) ' +
|
|
|
|
'AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10B350', None, 'iOS'),
|
|
|
|
('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) ' +
|
|
|
|
'AppleWebKit/537.36 (KHTML, like Gecko) ' +
|
2017-08-26 09:33:47 +02:00
|
|
|
'Chrome/56.0.2924.87 Safari/537.36', 'Chrome', 'macOS'),
|
2017-01-30 23:19:38 +01:00
|
|
|
('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) ' +
|
|
|
|
'AppleWebKit/602.3.12 (KHTML, like Gecko) ' +
|
2017-08-26 09:33:47 +02:00
|
|
|
'Version/10.0.2 Safari/602.3.12', 'Safari', 'macOS'),
|
2017-06-16 06:04:59 +02:00
|
|
|
('ZulipAndroid/1.0', 'Zulip', 'Android'),
|
2017-07-07 22:34:25 +02:00
|
|
|
('ZulipMobile/1.0.12 (Android 7.1.1)', 'Zulip', 'Android'),
|
|
|
|
('ZulipMobile/0.7.1.1 (iOS 10.3.1)', 'Zulip', 'iOS'),
|
2017-07-03 19:10:50 +02:00
|
|
|
('ZulipElectron/1.1.0-beta Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' +
|
|
|
|
'AppleWebKit/537.36 (KHTML, like Gecko) Zulip/1.1.0-beta ' +
|
|
|
|
'Chrome/56.0.2924.87 Electron/1.6.8 Safari/537.36', 'Zulip', 'Windows'),
|
2017-06-22 06:34:58 +02:00
|
|
|
('Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, '
|
|
|
|
'like Gecko) Ubuntu/11.10 Chromium/16.0.912.77 '
|
|
|
|
'Chrome/16.0.912.77 Safari/535.7', 'Chromium', 'Linux'),
|
2017-06-22 06:34:26 +02:00
|
|
|
('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 '
|
|
|
|
'(KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 '
|
|
|
|
'OPR/15.0.1147.100', 'Opera', 'Windows'),
|
2017-06-22 06:30:33 +02:00
|
|
|
('Mozilla/5.0 (Windows NT 10.0; <64-bit tags>) AppleWebKit/'
|
|
|
|
'<WebKit Rev> (KHTML, like Gecko) Chrome/<Chrome Rev> Safari'
|
|
|
|
'/<WebKit Rev> Edge/<EdgeHTML Rev>.'
|
|
|
|
'<Windows Build>', 'Edge', 'Windows'),
|
2018-09-21 18:10:53 +02:00
|
|
|
('Mozilla/5.0 (X11; CrOS x86_64 10895.56.0) AppleWebKit/537.36'
|
|
|
|
'(KHTML, like Gecko) Chrome/69.0.3497.95 Safari/537.36',
|
|
|
|
'Chrome', 'ChromeOS'),
|
2017-01-30 23:19:38 +01:00
|
|
|
('', None, None),
|
|
|
|
]
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_get_browser_on_new_login(self) -> None:
|
2017-01-30 23:19:38 +01:00
|
|
|
for user_agent in self.user_agents:
|
|
|
|
device_browser = get_device_browser(user_agent[0])
|
|
|
|
self.assertEqual(device_browser, user_agent[1])
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_get_os_on_new_login(self) -> None:
|
2017-01-30 23:19:38 +01:00
|
|
|
for user_agent in self.user_agents:
|
|
|
|
device_os = get_device_os(user_agent[0])
|
|
|
|
self.assertEqual(device_os, user_agent[2])
|
2017-05-24 01:20:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
class TestNotifyNewUser(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_notify_realm_of_new_user(self) -> None:
|
2017-05-24 23:13:10 +02:00
|
|
|
new_user = self.example_user('cordelia')
|
2017-10-04 02:07:44 +02:00
|
|
|
stream = self.make_stream(Realm.INITIAL_PRIVATE_STREAM_NAME)
|
2017-10-04 02:01:22 +02:00
|
|
|
new_user.realm.signup_notifications_stream_id = stream.id
|
2017-05-24 23:13:10 +02:00
|
|
|
new_user.realm.save()
|
|
|
|
new_user = self.example_user('cordelia')
|
|
|
|
notify_new_user(new_user)
|
|
|
|
|
|
|
|
message = self.get_last_message()
|
|
|
|
self.assertEqual(message.recipient.type, Recipient.STREAM)
|
|
|
|
actual_stream = Stream.objects.get(id=message.recipient.type_id)
|
2017-10-04 02:07:44 +02:00
|
|
|
self.assertEqual(actual_stream.name, Realm.INITIAL_PRIVATE_STREAM_NAME)
|
2020-06-13 08:59:37 +02:00
|
|
|
self.assertIn(f'@_**Cordelia Lear|{new_user.id}** just signed up for Zulip.', message.content)
|