2016-12-01 08:54:21 +01:00
|
|
|
import datetime
|
2020-06-11 00:54:34 +02:00
|
|
|
import re
|
|
|
|
import time
|
|
|
|
import urllib
|
2023-03-09 02:27:46 +01:00
|
|
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union
|
2020-06-11 00:54:34 +02:00
|
|
|
from unittest.mock import MagicMock, patch
|
2020-09-13 00:37:41 +02:00
|
|
|
from urllib.parse import urlencode
|
2018-06-19 14:50:36 +02:00
|
|
|
|
2020-08-07 01:09:47 +02:00
|
|
|
import orjson
|
2014-07-03 18:18:00 +02:00
|
|
|
from django.conf import settings
|
2020-12-19 20:04:57 +01:00
|
|
|
from django.contrib.auth.views import PasswordResetConfirmView
|
2017-03-31 08:41:14 +02:00
|
|
|
from django.contrib.contenttypes.models import ContentType
|
2020-06-11 00:54:34 +02:00
|
|
|
from django.core.exceptions import ValidationError
|
2023-03-09 02:27:46 +01:00
|
|
|
from django.http import HttpResponse, HttpResponseBase
|
2022-10-08 06:10:17 +02:00
|
|
|
from django.template.response import TemplateResponse
|
2021-04-29 00:34:05 +02:00
|
|
|
from django.test import Client, override_settings
|
2022-05-30 06:18:57 +02:00
|
|
|
from django.utils import translation
|
|
|
|
from django.utils.translation import gettext as _
|
2014-01-31 21:08:40 +01:00
|
|
|
|
2023-03-10 11:42:18 +01:00
|
|
|
from confirmation import settings as confirmation_settings
|
2020-06-11 00:54:34 +02:00
|
|
|
from confirmation.models import (
|
|
|
|
Confirmation,
|
|
|
|
one_click_unsubscribe_link,
|
2014-01-31 21:08:40 +01:00
|
|
|
)
|
2022-04-14 23:58:15 +02:00
|
|
|
from zerver.actions.create_realm import do_change_realm_subdomain, do_create_realm
|
2023-03-09 02:27:46 +01:00
|
|
|
from zerver.actions.create_user import add_new_user_history
|
2022-04-14 23:34:23 +02:00
|
|
|
from zerver.actions.default_streams import (
|
|
|
|
do_add_default_stream,
|
|
|
|
do_create_default_stream_group,
|
|
|
|
get_default_streams_for_realm,
|
|
|
|
)
|
2022-04-14 23:57:15 +02:00
|
|
|
from zerver.actions.realm_settings import (
|
|
|
|
do_deactivate_realm,
|
2022-10-11 22:15:55 +02:00
|
|
|
do_set_realm_authentication_methods,
|
2022-04-14 23:57:15 +02:00
|
|
|
do_set_realm_property,
|
|
|
|
do_set_realm_user_default_setting,
|
|
|
|
)
|
2022-04-14 23:48:28 +02:00
|
|
|
from zerver.actions.users import change_user_is_active, do_change_user_role, do_deactivate_user
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.decorator import do_two_factor_login
|
|
|
|
from zerver.forms import HomepageForm, check_subdomain_available
|
2023-03-15 18:09:26 +01:00
|
|
|
from zerver.lib.email_notifications import enqueue_welcome_emails
|
2021-04-09 23:09:56 +02:00
|
|
|
from zerver.lib.i18n import get_default_language_for_new_user
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.initial_password import initial_password
|
|
|
|
from zerver.lib.mobile_auth_otp import (
|
|
|
|
ascii_to_hex,
|
|
|
|
hex_to_ascii,
|
|
|
|
is_valid_otp,
|
|
|
|
otp_decrypt_api_key,
|
|
|
|
otp_encrypt_api_key,
|
|
|
|
xor_hex_strings,
|
2017-03-21 18:08:40 +01:00
|
|
|
)
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.name_restrictions import is_disposable_domain
|
2019-12-30 02:21:51 +01:00
|
|
|
from zerver.lib.rate_limiter import add_ratelimit_rule, remove_ratelimit_rule
|
2021-04-03 12:14:01 +02:00
|
|
|
from zerver.lib.send_email import (
|
2022-11-17 09:30:48 +01:00
|
|
|
EmailNotDeliveredError,
|
2021-04-03 12:14:01 +02:00
|
|
|
FromAddress,
|
|
|
|
send_future_email,
|
|
|
|
)
|
2019-01-16 09:59:01 +01:00
|
|
|
from zerver.lib.stream_subscription import get_stream_subscriptions_for_user
|
2020-03-24 14:47:41 +01:00
|
|
|
from zerver.lib.streams import create_stream_if_needed
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.lib.subdomains import is_root_domain_available
|
|
|
|
from zerver.lib.test_classes import ZulipTestCase
|
|
|
|
from zerver.lib.test_helpers import (
|
2021-04-09 23:09:56 +02:00
|
|
|
HostRequestMock,
|
2020-06-11 00:54:34 +02:00
|
|
|
avatar_disk_path,
|
tests: Fix queries_captured to clear cache up front.
Before this change we were clearing the cache on
every SQL usage.
The code to do this was added in February 2017
in 6db4879f9c9fd6941d3aa2af6138ea75aa6675a6.
Now we clear the cache just one time, but before
the action/request under test.
Tests that want to count queries with a warm
cache now specify keep_cache_warm=True. Those
tests were particularly flawed before this change.
In general, the old code both over-counted and
under-counted queries.
It under-counted SQL usage for requests that were
able to pull some data out of a warm cache before
they did any SQL. Typically this would have bypassed
the initial query to get UserProfile, so you
will see several off-by-one fixes.
The old code over-counted SQL usage to the extent
that it's a rather extreme assumption that during
an action itself, the entries that you put into
the cache will get thrown away. And that's essentially
what the prior code simulated.
Now, it's still bad if an action keeps hitting the
cache for no reason, but it's not as bad as hitting
the database. There doesn't appear to be any evidence
of us doing something silly like fetching the same
data from the cache in a loop, but there are
opportunities to prevent second or third round
trips to the cache for the same object, if we
can re-structure the code so that the same caller
doesn't have two callees get the same data.
Note that for invites, we have some cache hits
that are due to the nature of how we serialize
data to our queue processor--we generally just
serialize ids, and then re-fetch objects when
we pop them off the queue.
2020-11-04 12:02:00 +01:00
|
|
|
cache_tries_captured,
|
2020-06-11 00:54:34 +02:00
|
|
|
find_key_by_email,
|
|
|
|
get_test_image_file,
|
|
|
|
load_subdomain_token,
|
2020-07-08 02:22:52 +02:00
|
|
|
message_stream_count,
|
|
|
|
most_recent_message,
|
|
|
|
most_recent_usermessage,
|
2023-03-01 07:34:25 +01:00
|
|
|
reset_email_visibility_to_everyone_in_zulip_realm,
|
2016-11-10 19:30:09 +01:00
|
|
|
)
|
2020-06-11 00:54:34 +02:00
|
|
|
from zerver.models import (
|
|
|
|
CustomProfileField,
|
|
|
|
CustomProfileFieldValue,
|
|
|
|
DefaultStream,
|
|
|
|
Message,
|
2023-03-10 11:42:18 +01:00
|
|
|
PreregistrationRealm,
|
2020-06-11 00:54:34 +02:00
|
|
|
PreregistrationUser,
|
|
|
|
Realm,
|
2021-04-20 12:29:19 +02:00
|
|
|
RealmAuditLog,
|
2021-09-17 18:11:37 +02:00
|
|
|
RealmUserDefault,
|
2020-06-11 00:54:34 +02:00
|
|
|
Recipient,
|
|
|
|
ScheduledEmail,
|
|
|
|
Stream,
|
|
|
|
Subscription,
|
|
|
|
UserMessage,
|
|
|
|
UserProfile,
|
|
|
|
flush_per_request_caches,
|
|
|
|
get_realm,
|
2021-07-16 22:11:10 +02:00
|
|
|
get_stream,
|
2020-06-11 00:54:34 +02:00
|
|
|
get_system_bot,
|
|
|
|
get_user,
|
|
|
|
get_user_by_delivery_email,
|
|
|
|
)
|
|
|
|
from zerver.views.auth import redirect_and_log_into_subdomain, start_two_factor_auth
|
|
|
|
from zerver.views.development.registration import confirmation_key
|
2022-10-11 22:15:55 +02:00
|
|
|
from zproject.backends import ExternalAuthDataDict, ExternalAuthResult, email_auth_enabled
|
2017-02-12 21:21:31 +01:00
|
|
|
|
2022-06-08 04:52:09 +02:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
|
|
|
|
|
2014-01-31 21:08:40 +01:00
|
|
|
|
2017-04-20 08:30:50 +02:00
|
|
|
class RedirectAndLogIntoSubdomainTestCase(ZulipTestCase):
|
2020-02-23 18:58:08 +01:00
|
|
|
def test_data(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
name = user_profile.full_name
|
|
|
|
email = user_profile.delivery_email
|
|
|
|
response = redirect_and_log_into_subdomain(ExternalAuthResult(user_profile=user_profile))
|
2017-10-27 02:45:38 +02:00
|
|
|
data = load_subdomain_token(response)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertDictEqual(
|
|
|
|
data,
|
2021-02-12 08:20:45 +01:00
|
|
|
{"full_name": name, "email": email, "subdomain": realm.subdomain, "is_signup": False},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-04-20 08:30:50 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
data_dict = ExternalAuthDataDict(is_signup=True, multiuse_object_key="key")
|
2021-02-12 08:19:30 +01:00
|
|
|
response = redirect_and_log_into_subdomain(
|
|
|
|
ExternalAuthResult(user_profile=user_profile, data_dict=data_dict)
|
|
|
|
)
|
2017-10-27 02:45:38 +02:00
|
|
|
data = load_subdomain_token(response)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertDictEqual(
|
|
|
|
data,
|
|
|
|
{
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": name,
|
|
|
|
"email": email,
|
|
|
|
"subdomain": realm.subdomain,
|
2021-02-12 08:19:30 +01:00
|
|
|
# the email has an account at the subdomain,
|
|
|
|
# so is_signup get overridden to False:
|
2021-02-12 08:20:45 +01:00
|
|
|
"is_signup": False,
|
|
|
|
"multiuse_object_key": "key",
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
data_dict = ExternalAuthDataDict(
|
|
|
|
email=self.nonreg_email("alice"),
|
|
|
|
full_name="Alice",
|
|
|
|
subdomain=realm.subdomain,
|
|
|
|
is_signup=True,
|
|
|
|
full_name_validated=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
multiuse_object_key="key",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-02-23 18:58:08 +01:00
|
|
|
response = redirect_and_log_into_subdomain(ExternalAuthResult(data_dict=data_dict))
|
2019-11-01 00:00:36 +01:00
|
|
|
data = load_subdomain_token(response)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertDictEqual(
|
|
|
|
data,
|
|
|
|
{
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "Alice",
|
|
|
|
"email": self.nonreg_email("alice"),
|
|
|
|
"full_name_validated": True,
|
|
|
|
"subdomain": realm.subdomain,
|
|
|
|
"is_signup": True,
|
|
|
|
"multiuse_object_key": "key",
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2017-04-20 08:30:50 +02:00
|
|
|
|
2017-08-25 06:32:57 +02:00
|
|
|
class DeactivationNoticeTestCase(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_redirection_for_deactivated_realm(self) -> None:
|
2017-08-25 06:32:57 +02:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.deactivated = True
|
|
|
|
realm.save(update_fields=["deactivated"])
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
for url in ("/register/", "/login/"):
|
2017-10-02 22:43:43 +02:00
|
|
|
result = self.client_get(url)
|
2022-06-24 00:00:10 +02:00
|
|
|
self.assertRedirects(result, "/accounts/deactivated/", status_code=302)
|
2017-08-25 06:32:57 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_redirection_for_active_realm(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
for url in ("/register/", "/login/"):
|
2017-10-02 22:43:43 +02:00
|
|
|
result = self.client_get(url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
2017-08-25 06:32:57 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_deactivation_notice_when_realm_is_active(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_get("/accounts/deactivated/")
|
2022-06-24 00:00:10 +02:00
|
|
|
self.assertRedirects(result, "/devlogin/", status_code=302)
|
2017-08-25 06:32:57 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_deactivation_notice_when_deactivated(self) -> None:
|
2017-08-25 06:32:57 +02:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.deactivated = True
|
|
|
|
realm.save(update_fields=["deactivated"])
|
|
|
|
|
2022-06-24 00:00:10 +02:00
|
|
|
result = self.client_get("/login/", follow=True)
|
2022-10-08 06:10:17 +02:00
|
|
|
self.assertEqual(result.redirect_chain[-1], ("/accounts/deactivated/", 302))
|
2017-10-02 22:43:43 +02:00
|
|
|
self.assertIn("Zulip Dev, has been deactivated.", result.content.decode())
|
2020-12-12 12:23:48 +01:00
|
|
|
self.assertNotIn("It has moved to", result.content.decode())
|
|
|
|
|
|
|
|
def test_deactivation_notice_when_deactivated_and_deactivated_redirect_is_set(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.deactivated = True
|
|
|
|
realm.deactivated_redirect = "http://example.zulipchat.com"
|
|
|
|
realm.save(update_fields=["deactivated", "deactivated_redirect"])
|
|
|
|
|
2022-06-24 00:00:10 +02:00
|
|
|
result = self.client_get("/login/", follow=True)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
|
|
|
'It has moved to <a href="http://example.zulipchat.com">http://example.zulipchat.com</a>.',
|
|
|
|
result.content.decode(),
|
|
|
|
)
|
2017-08-25 06:32:57 +02:00
|
|
|
|
2020-12-18 20:17:20 +01:00
|
|
|
def test_deactivation_notice_when_realm_subdomain_is_changed(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
2020-12-06 20:04:33 +01:00
|
|
|
do_change_realm_subdomain(realm, "new-subdomain-name", acting_user=None)
|
2020-12-18 20:17:20 +01:00
|
|
|
|
2022-06-24 00:00:10 +02:00
|
|
|
result = self.client_get("/login/", follow=True)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
|
|
|
'It has moved to <a href="http://new-subdomain-name.testserver">http://new-subdomain-name.testserver</a>.',
|
|
|
|
result.content.decode(),
|
|
|
|
)
|
2020-12-18 20:17:20 +01:00
|
|
|
|
2022-06-23 23:57:39 +02:00
|
|
|
def test_no_deactivation_notice_with_no_rediect(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
do_change_realm_subdomain(
|
|
|
|
realm, "new-subdomain-name", acting_user=None, add_deactivated_redirect=False
|
|
|
|
)
|
|
|
|
|
|
|
|
result = self.client_get("/login/", follow=True)
|
|
|
|
self.assertEqual(result.status_code, 404)
|
|
|
|
self.assertNotIn(
|
|
|
|
"new-subdomain-name",
|
|
|
|
result.content.decode(),
|
|
|
|
)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
def test_deactivated_redirect_field_of_placeholder_realms_are_modified_on_changing_subdomain_multiple_times(
|
|
|
|
self,
|
|
|
|
) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2020-12-06 20:04:33 +01:00
|
|
|
do_change_realm_subdomain(realm, "new-name-1", acting_user=None)
|
2020-12-18 20:17:20 +01:00
|
|
|
|
2022-06-24 00:00:10 +02:00
|
|
|
result = self.client_get("/login/", follow=True)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
|
|
|
'It has moved to <a href="http://new-name-1.testserver">http://new-name-1.testserver</a>.',
|
|
|
|
result.content.decode(),
|
|
|
|
)
|
2020-12-18 20:17:20 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("new-name-1")
|
2020-12-06 20:04:33 +01:00
|
|
|
do_change_realm_subdomain(realm, "new-name-2", acting_user=None)
|
2022-06-24 00:00:10 +02:00
|
|
|
result = self.client_get("/login/", follow=True)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
|
|
|
'It has moved to <a href="http://new-name-2.testserver">http://new-name-2.testserver</a>.',
|
|
|
|
result.content.decode(),
|
|
|
|
)
|
|
|
|
|
2020-12-18 20:17:20 +01:00
|
|
|
|
2016-11-11 05:33:30 +01:00
|
|
|
class AddNewUserHistoryTest(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_add_new_user_history_race(self) -> None:
|
2016-11-11 05:33:30 +01:00
|
|
|
"""Sends a message during user creation"""
|
|
|
|
# Create a user who hasn't had historical messages added
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
stream = Stream.objects.get(realm=realm, name="Denmark")
|
2019-02-24 04:40:44 +01:00
|
|
|
DefaultStream.objects.create(stream=stream, realm=realm)
|
2019-08-26 22:39:33 +02:00
|
|
|
# Make sure at least 3 messages are sent to Denmark and it's a default stream.
|
2021-02-12 08:20:45 +01:00
|
|
|
message_id = self.send_stream_message(self.example_user("hamlet"), stream.name, "test 1")
|
|
|
|
self.send_stream_message(self.example_user("hamlet"), stream.name, "test 2")
|
|
|
|
self.send_stream_message(self.example_user("hamlet"), stream.name, "test 3")
|
2019-08-26 22:39:33 +02:00
|
|
|
|
2022-04-14 23:53:15 +02:00
|
|
|
with patch("zerver.actions.create_user.add_new_user_history"):
|
2021-02-12 08:20:45 +01:00
|
|
|
self.register(self.nonreg_email("test"), "test")
|
|
|
|
user_profile = self.nonreg_user("test")
|
2016-11-11 05:33:30 +01:00
|
|
|
subs = Subscription.objects.select_related("recipient").filter(
|
2021-02-12 08:19:30 +01:00
|
|
|
user_profile=user_profile, recipient__type=Recipient.STREAM
|
|
|
|
)
|
2016-11-11 05:33:30 +01:00
|
|
|
streams = Stream.objects.filter(id__in=[sub.recipient.type_id for sub in subs])
|
2019-08-26 22:39:33 +02:00
|
|
|
|
|
|
|
# Sent a message afterwards to trigger a race between message
|
|
|
|
# sending and `add_new_user_history`.
|
2021-02-12 08:19:30 +01:00
|
|
|
race_message_id = self.send_stream_message(
|
2021-02-12 08:20:45 +01:00
|
|
|
self.example_user("hamlet"), streams[0].name, "test"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-08-26 22:39:33 +02:00
|
|
|
|
|
|
|
# Overwrite ONBOARDING_UNREAD_MESSAGES to 2
|
|
|
|
ONBOARDING_UNREAD_MESSAGES = 2
|
2022-04-14 23:53:15 +02:00
|
|
|
with patch(
|
|
|
|
"zerver.actions.create_user.ONBOARDING_UNREAD_MESSAGES", ONBOARDING_UNREAD_MESSAGES
|
|
|
|
):
|
2019-08-26 22:39:33 +02:00
|
|
|
add_new_user_history(user_profile, streams)
|
|
|
|
|
|
|
|
# Our first message is in the user's history
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
|
|
|
UserMessage.objects.filter(user_profile=user_profile, message_id=message_id).exists()
|
|
|
|
)
|
2019-08-26 22:39:33 +02:00
|
|
|
# The race message is in the user's history and marked unread.
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
|
|
|
UserMessage.objects.filter(
|
|
|
|
user_profile=user_profile, message_id=race_message_id
|
|
|
|
).exists()
|
|
|
|
)
|
|
|
|
self.assertFalse(
|
|
|
|
UserMessage.objects.get(
|
|
|
|
user_profile=user_profile, message_id=race_message_id
|
|
|
|
).flags.read.is_set
|
|
|
|
)
|
2019-08-26 22:39:33 +02:00
|
|
|
|
|
|
|
# Verify that the ONBOARDING_UNREAD_MESSAGES latest messages
|
|
|
|
# that weren't the race message are marked as unread.
|
2021-02-12 08:19:30 +01:00
|
|
|
latest_messages = (
|
|
|
|
UserMessage.objects.filter(
|
|
|
|
user_profile=user_profile,
|
|
|
|
message__recipient__type=Recipient.STREAM,
|
|
|
|
)
|
|
|
|
.exclude(message_id=race_message_id)
|
2021-02-12 08:20:45 +01:00
|
|
|
.order_by("-message_id")[0:ONBOARDING_UNREAD_MESSAGES]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(latest_messages, 2)
|
2019-08-26 22:39:33 +02:00
|
|
|
for msg in latest_messages:
|
|
|
|
self.assertFalse(msg.flags.read.is_set)
|
|
|
|
|
|
|
|
# Verify that older messages are correctly marked as read.
|
2021-02-12 08:19:30 +01:00
|
|
|
older_messages = (
|
|
|
|
UserMessage.objects.filter(
|
|
|
|
user_profile=user_profile,
|
|
|
|
message__recipient__type=Recipient.STREAM,
|
|
|
|
)
|
|
|
|
.exclude(message_id=race_message_id)
|
2021-02-12 08:20:45 +01:00
|
|
|
.order_by("-message_id")[ONBOARDING_UNREAD_MESSAGES : ONBOARDING_UNREAD_MESSAGES + 1]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-07-13 19:42:37 +02:00
|
|
|
self.assertGreater(len(older_messages), 0)
|
2019-08-26 22:39:33 +02:00
|
|
|
for msg in older_messages:
|
|
|
|
self.assertTrue(msg.flags.read.is_set)
|
2016-11-11 05:33:30 +01:00
|
|
|
|
2020-07-08 02:22:52 +02:00
|
|
|
def test_auto_subbed_to_personals(self) -> None:
|
|
|
|
"""
|
|
|
|
Newly created users are auto-subbed to the ability to receive
|
|
|
|
personals.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
test_email = self.nonreg_email("test")
|
2020-07-08 02:22:52 +02:00
|
|
|
self.register(test_email, "test")
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.nonreg_user("test")
|
2020-07-08 02:22:52 +02:00
|
|
|
old_messages_count = message_stream_count(user_profile)
|
|
|
|
self.send_personal_message(user_profile, user_profile)
|
|
|
|
new_messages_count = message_stream_count(user_profile)
|
|
|
|
self.assertEqual(new_messages_count, old_messages_count + 1)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
recipient = Recipient.objects.get(type_id=user_profile.id, type=Recipient.PERSONAL)
|
2020-07-08 02:22:52 +02:00
|
|
|
message = most_recent_message(user_profile)
|
|
|
|
self.assertEqual(message.recipient, recipient)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.models.get_display_recipient", return_value="recip"):
|
2020-07-08 02:22:52 +02:00
|
|
|
self.assertEqual(
|
2023-03-08 22:18:59 +01:00
|
|
|
repr(message),
|
2021-02-12 08:20:45 +01:00
|
|
|
"<Message: recip / / "
|
2023-03-08 22:18:59 +01:00
|
|
|
"<UserProfile: {} {!r}>>".format(user_profile.email, user_profile.realm),
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-07-08 02:22:52 +02:00
|
|
|
|
|
|
|
user_message = most_recent_usermessage(user_profile)
|
|
|
|
self.assertEqual(
|
2023-03-08 22:18:59 +01:00
|
|
|
repr(user_message),
|
2021-02-12 08:20:45 +01:00
|
|
|
f"<UserMessage: recip / {user_profile.email} ([])>",
|
2020-07-08 02:22:52 +02:00
|
|
|
)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2018-03-18 20:56:00 +01:00
|
|
|
class InitialPasswordTest(ZulipTestCase):
|
|
|
|
def test_none_initial_password_salt(self) -> None:
|
|
|
|
with self.settings(INITIAL_PASSWORD_SALT=None):
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIsNone(initial_password("test@test.com"))
|
2018-03-18 20:56:00 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-09-13 22:49:03 +02:00
|
|
|
class PasswordResetTest(ZulipTestCase):
|
|
|
|
"""
|
|
|
|
Log in, reset password, log out, log in with new password.
|
|
|
|
"""
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
def get_reset_mail_body(self, subdomain: str = "zulip") -> str:
|
2020-06-05 23:26:35 +02:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-06-05 23:26:35 +02:00
|
|
|
[message] = outbox
|
2021-01-26 04:20:36 +01:00
|
|
|
self.assertEqual(self.email_envelope_from(message), settings.NOREPLY_EMAIL_ADDRESS)
|
2022-05-30 06:18:57 +02:00
|
|
|
# The email might be sent in different languages for i18n testing
|
2020-06-05 23:26:35 +02:00
|
|
|
self.assertRegex(
|
2021-01-26 04:20:36 +01:00
|
|
|
self.email_display_from(message),
|
2022-05-30 06:18:57 +02:00
|
|
|
rf'^{_("Zulip Account Security")} <{self.TOKENIZED_NOREPLY_REGEX}>\Z',
|
2020-06-05 23:26:35 +02:00
|
|
|
)
|
2020-06-14 13:32:38 +02:00
|
|
|
self.assertIn(f"{subdomain}.testserver", message.extra_headers["List-Id"])
|
|
|
|
|
2020-06-05 23:26:35 +02:00
|
|
|
return message.body
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_password_reset(self) -> None:
|
2020-03-06 18:40:46 +01:00
|
|
|
user = self.example_user("hamlet")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = user.delivery_email
|
2016-09-13 22:49:03 +02:00
|
|
|
old_password = initial_password(email)
|
2020-07-05 01:38:05 +02:00
|
|
|
assert old_password is not None
|
2016-09-13 22:49:03 +02:00
|
|
|
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2016-09-13 22:49:03 +02:00
|
|
|
|
2016-12-01 08:54:21 +01:00
|
|
|
# test password reset template
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assert_in_response("Reset your password", result)
|
2016-12-01 08:54:21 +01:00
|
|
|
|
2016-09-13 22:49:03 +02:00
|
|
|
# start the password reset process by supplying an email address
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email})
|
2016-09-13 22:49:03 +02:00
|
|
|
|
|
|
|
# check the redirect link telling you to check mail for password reset link
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/"))
|
2016-09-13 22:49:03 +02:00
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
|
2018-09-16 02:34:57 +02:00
|
|
|
self.assert_in_response("Check your email in a few minutes to finish the process.", result)
|
2016-09-13 22:49:03 +02:00
|
|
|
|
2017-07-05 21:29:27 +02:00
|
|
|
# Check that the password reset email is from a noreply address.
|
2020-06-05 23:26:35 +02:00
|
|
|
body = self.get_reset_mail_body()
|
|
|
|
self.assertIn("reset your password", body)
|
2017-07-05 21:29:27 +02:00
|
|
|
|
2016-09-23 04:23:48 +02:00
|
|
|
# Visit the password reset link.
|
2017-10-30 22:56:14 +01:00
|
|
|
password_reset_url = self.get_confirmation_url_from_outbox(
|
2021-02-12 08:19:30 +01:00
|
|
|
email, url_pattern=settings.EXTERNAL_HOST + r"(\S\S+)"
|
|
|
|
)
|
2016-09-13 22:49:03 +02:00
|
|
|
result = self.client_get(password_reset_url)
|
2020-02-02 13:51:39 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(f"/{PasswordResetConfirmView.reset_url_token}/")
|
|
|
|
)
|
2020-02-02 13:51:39 +01:00
|
|
|
|
2022-05-29 21:12:13 +02:00
|
|
|
final_reset_url = result["Location"]
|
2020-02-02 13:51:39 +01:00
|
|
|
result = self.client_get(final_reset_url)
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2016-09-13 22:49:03 +02:00
|
|
|
|
|
|
|
# Reset your password
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
with self.settings(PASSWORD_MIN_LENGTH=3, PASSWORD_MIN_GUESSES=1000):
|
|
|
|
# Verify weak passwords don't work.
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
final_reset_url, {"new_password1": "easy", "new_password2": "easy"}
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
self.assert_in_response("The password is too weak.", result)
|
|
|
|
|
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
final_reset_url, {"new_password1": "f657gdGGk9", "new_password2": "f657gdGGk9"}
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
# password reset succeeded
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertTrue(result["Location"].endswith("/password/done/"))
|
2016-09-13 22:49:03 +02:00
|
|
|
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
# log back in with new password
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login_by_email(email, password="f657gdGGk9")
|
|
|
|
user_profile = self.example_user("hamlet")
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
2016-09-13 22:49:03 +02:00
|
|
|
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
# make sure old password no longer works
|
2020-03-06 18:40:46 +01:00
|
|
|
self.assert_login_failure(email, password=old_password)
|
2016-09-13 22:49:03 +02:00
|
|
|
|
2021-08-19 09:55:54 +02:00
|
|
|
@patch("django.http.HttpRequest.get_host")
|
|
|
|
def test_password_reset_page_redirects_for_root_alias_when_root_domain_landing_page_is_enabled(
|
|
|
|
self, mock_get_host: MagicMock
|
|
|
|
) -> None:
|
|
|
|
mock_get_host.return_value = "alias.testserver"
|
|
|
|
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True, ROOT_SUBDOMAIN_ALIASES=["alias"]):
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(
|
|
|
|
result["Location"], "/accounts/go/?next=%2Faccounts%2Fpassword%2Freset%2F"
|
|
|
|
)
|
2021-08-19 09:55:54 +02:00
|
|
|
|
|
|
|
mock_get_host.return_value = "www.testserver"
|
|
|
|
with self.settings(
|
|
|
|
ROOT_DOMAIN_LANDING_PAGE=True,
|
|
|
|
):
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(
|
|
|
|
result["Location"], "/accounts/go/?next=%2Faccounts%2Fpassword%2Freset%2F"
|
|
|
|
)
|
2021-08-19 09:55:54 +02:00
|
|
|
|
|
|
|
@patch("django.http.HttpRequest.get_host")
|
|
|
|
def test_password_reset_page_redirects_for_root_domain_when_root_domain_landing_page_is_enabled(
|
|
|
|
self, mock_get_host: MagicMock
|
|
|
|
) -> None:
|
|
|
|
mock_get_host.return_value = "testserver"
|
|
|
|
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True):
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(
|
|
|
|
result["Location"], "/accounts/go/?next=%2Faccounts%2Fpassword%2Freset%2F"
|
|
|
|
)
|
2021-08-19 09:55:54 +02:00
|
|
|
|
2022-08-27 19:16:55 +02:00
|
|
|
mock_get_host.return_value = "www.zulip.example.com"
|
2021-08-19 09:55:54 +02:00
|
|
|
with self.settings(
|
|
|
|
ROOT_DOMAIN_LANDING_PAGE=True,
|
2022-08-27 19:16:55 +02:00
|
|
|
EXTERNAL_HOST="www.zulip.example.com",
|
2021-08-19 09:55:54 +02:00
|
|
|
):
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(
|
|
|
|
result["Location"], "/accounts/go/?next=%2Faccounts%2Fpassword%2Freset%2F"
|
|
|
|
)
|
2021-08-19 09:55:54 +02:00
|
|
|
|
|
|
|
@patch("django.http.HttpRequest.get_host")
|
|
|
|
def test_password_reset_page_works_for_root_alias_when_root_domain_landing_page_is_not_enabled(
|
|
|
|
self, mock_get_host: MagicMock
|
|
|
|
) -> None:
|
|
|
|
mock_get_host.return_value = "alias.testserver"
|
|
|
|
with self.settings(ROOT_SUBDOMAIN_ALIASES=["alias"]):
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
mock_get_host.return_value = "www.testserver"
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
@patch("django.http.HttpRequest.get_host")
|
|
|
|
def test_password_reset_page_works_for_root_domain_when_root_domain_landing_page_is_not_enabled(
|
|
|
|
self, mock_get_host: MagicMock
|
|
|
|
) -> None:
|
|
|
|
mock_get_host.return_value = "testserver"
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2022-08-27 19:16:55 +02:00
|
|
|
mock_get_host.return_value = "www.zulip.example.com"
|
|
|
|
with self.settings(EXTERNAL_HOST="www.zulip.example.com", ROOT_SUBDOMAIN_ALIASES=[]):
|
2021-08-19 09:55:54 +02:00
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
@patch("django.http.HttpRequest.get_host")
|
|
|
|
def test_password_reset_page_works_always_for_subdomains(
|
|
|
|
self, mock_get_host: MagicMock
|
|
|
|
) -> None:
|
|
|
|
mock_get_host.return_value = "lear.testserver"
|
|
|
|
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True):
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
result = self.client_get("/accounts/password/reset/")
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_password_reset_for_non_existent_user(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "nonexisting@mars.com"
|
2017-08-11 07:55:51 +02:00
|
|
|
|
2017-11-18 03:17:50 +01:00
|
|
|
# start the password reset process by supplying an email address
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email})
|
2017-08-11 07:55:51 +02:00
|
|
|
|
|
|
|
# check the redirect link telling you to check mail for password reset link
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/"))
|
2017-08-11 07:55:51 +02:00
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
|
2018-09-16 02:34:57 +02:00
|
|
|
self.assert_in_response("Check your email in a few minutes to finish the process.", result)
|
2017-08-11 07:55:51 +02:00
|
|
|
|
|
|
|
# Check that the password reset email is from a noreply address.
|
2020-06-05 23:26:35 +02:00
|
|
|
body = self.get_reset_mail_body()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIn("Somebody (possibly you) requested a new password", body)
|
|
|
|
self.assertIn("You do not have an account", body)
|
|
|
|
self.assertIn("safely ignore", body)
|
|
|
|
self.assertNotIn("reset your password", body)
|
|
|
|
self.assertNotIn("deactivated", body)
|
2017-08-11 07:55:51 +02:00
|
|
|
|
2018-05-21 05:02:27 +02:00
|
|
|
def test_password_reset_for_deactivated_user(self) -> None:
|
|
|
|
user_profile = self.example_user("hamlet")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = user_profile.delivery_email
|
2021-03-27 06:02:12 +01:00
|
|
|
do_deactivate_user(user_profile, acting_user=None)
|
2018-05-21 05:02:27 +02:00
|
|
|
|
|
|
|
# start the password reset process by supplying an email address
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email})
|
2018-05-21 05:02:27 +02:00
|
|
|
|
|
|
|
# check the redirect link telling you to check mail for password reset link
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/"))
|
2018-05-21 05:02:27 +02:00
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
|
2018-09-16 02:34:57 +02:00
|
|
|
self.assert_in_response("Check your email in a few minutes to finish the process.", result)
|
2018-05-21 05:02:27 +02:00
|
|
|
|
|
|
|
# Check that the password reset email is from a noreply address.
|
2020-06-05 23:26:35 +02:00
|
|
|
body = self.get_reset_mail_body()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIn("Somebody (possibly you) requested a new password", body)
|
|
|
|
self.assertIn("has been deactivated", body)
|
|
|
|
self.assertIn("safely ignore", body)
|
|
|
|
self.assertNotIn("reset your password", body)
|
|
|
|
self.assertNotIn("not have an account", body)
|
2018-05-21 05:02:27 +02:00
|
|
|
|
|
|
|
def test_password_reset_with_deactivated_realm(self) -> None:
|
|
|
|
user_profile = self.example_user("hamlet")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = user_profile.delivery_email
|
2021-04-02 17:11:25 +02:00
|
|
|
do_deactivate_realm(user_profile.realm, acting_user=None)
|
2018-05-21 05:02:27 +02:00
|
|
|
|
|
|
|
# start the password reset process by supplying an email address
|
2020-12-23 21:45:16 +01:00
|
|
|
with self.assertLogs(level="INFO") as m:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email})
|
2020-12-23 21:45:16 +01:00
|
|
|
self.assertEqual(m.output, ["INFO:root:Realm is deactivated"])
|
2018-05-21 05:02:27 +02:00
|
|
|
|
|
|
|
# check the redirect link telling you to check mail for password reset link
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/"))
|
2018-05-21 05:02:27 +02:00
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
|
2018-09-16 02:34:57 +02:00
|
|
|
self.assert_in_response("Check your email in a few minutes to finish the process.", result)
|
2018-05-21 05:02:27 +02:00
|
|
|
|
|
|
|
# Check that the password reset email is from a noreply address.
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2018-05-21 05:02:27 +02:00
|
|
|
|
2019-12-30 21:13:02 +01:00
|
|
|
@override_settings(RATE_LIMITING=True)
|
|
|
|
def test_rate_limiting(self) -> None:
|
|
|
|
user_profile = self.example_user("hamlet")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = user_profile.delivery_email
|
2019-12-30 21:13:02 +01:00
|
|
|
from django.core.mail import outbox
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
add_ratelimit_rule(10, 2, domain="password_reset_form_by_email")
|
2019-12-30 21:13:02 +01:00
|
|
|
start_time = time.time()
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("time.time", return_value=start_time):
|
|
|
|
self.client_post("/accounts/password/reset/", {"email": email})
|
|
|
|
self.client_post("/accounts/password/reset/", {"email": email})
|
2019-12-30 21:13:02 +01:00
|
|
|
self.assert_length(outbox, 2)
|
|
|
|
|
|
|
|
# Too many password reset emails sent to the address, we won't send more.
|
2021-02-12 08:20:45 +01:00
|
|
|
with self.assertLogs(level="INFO") as info_logs:
|
|
|
|
self.client_post("/accounts/password/reset/", {"email": email})
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
info_logs.output,
|
2021-11-05 02:19:49 +01:00
|
|
|
[
|
|
|
|
"INFO:root:Too many password reset attempts for email hamlet@zulip.com from 127.0.0.1"
|
|
|
|
],
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-12-30 21:13:02 +01:00
|
|
|
self.assert_length(outbox, 2)
|
|
|
|
|
|
|
|
# Resetting for a different address works though.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.client_post("/accounts/password/reset/", {"email": self.example_email("othello")})
|
2019-12-30 21:13:02 +01:00
|
|
|
self.assert_length(outbox, 3)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.client_post("/accounts/password/reset/", {"email": self.example_email("othello")})
|
2019-12-30 21:13:02 +01:00
|
|
|
self.assert_length(outbox, 4)
|
|
|
|
|
|
|
|
# After time, password reset emails can be sent again.
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("time.time", return_value=start_time + 11):
|
|
|
|
self.client_post("/accounts/password/reset/", {"email": email})
|
|
|
|
self.client_post("/accounts/password/reset/", {"email": email})
|
2019-12-30 21:13:02 +01:00
|
|
|
self.assert_length(outbox, 6)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
remove_ratelimit_rule(10, 2, domain="password_reset_form_by_email")
|
2019-12-30 21:13:02 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_wrong_subdomain(self) -> None:
|
2017-05-25 01:40:26 +02:00
|
|
|
email = self.example_email("hamlet")
|
2017-11-18 03:30:07 +01:00
|
|
|
|
|
|
|
# start the password reset process by supplying an email address
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email}, subdomain="zephyr")
|
2017-04-24 12:19:54 +02:00
|
|
|
|
|
|
|
# check the redirect link telling you to check mail for password reset link
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/"))
|
2017-04-24 12:19:54 +02:00
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
|
2018-09-16 02:34:57 +02:00
|
|
|
self.assert_in_response("Check your email in a few minutes to finish the process.", result)
|
2017-04-24 12:19:54 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
body = self.get_reset_mail_body("zephyr")
|
|
|
|
self.assertIn("Somebody (possibly you) requested a new password", body)
|
|
|
|
self.assertIn("You do not have an account", body)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
|
|
|
"active accounts in the following organization(s).\nhttp://zulip.testserver", body
|
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIn("safely ignore", body)
|
|
|
|
self.assertNotIn("reset your password", body)
|
|
|
|
self.assertNotIn("deactivated", body)
|
2017-04-24 12:19:54 +02:00
|
|
|
|
2022-05-30 06:18:57 +02:00
|
|
|
def test_wrong_subdomain_i18n(self) -> None:
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
email = user_profile.delivery_email
|
|
|
|
|
|
|
|
# Send a password reset request with a different language to a wrong subdomain
|
|
|
|
result = self.client_post(
|
|
|
|
"/accounts/password/reset/",
|
|
|
|
{"email": email},
|
|
|
|
HTTP_ACCEPT_LANGUAGE="de",
|
|
|
|
subdomain="lear",
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
with translation.override("de"):
|
|
|
|
body = self.get_reset_mail_body("lear")
|
|
|
|
self.assertIn("hat ein neues Passwort", body)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_invalid_subdomain(self) -> None:
|
2017-05-25 01:40:26 +02:00
|
|
|
email = self.example_email("hamlet")
|
2017-04-24 12:19:54 +02:00
|
|
|
|
2017-11-18 03:30:07 +01:00
|
|
|
# start the password reset process by supplying an email address
|
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/password/reset/", {"email": email}, subdomain="invalid"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-04-24 12:19:54 +02:00
|
|
|
|
|
|
|
# check the redirect link telling you to check mail for password reset link
|
2019-03-12 01:56:52 +01:00
|
|
|
self.assertEqual(result.status_code, 404)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_response("There is no Zulip organization hosted at this subdomain.", result)
|
2017-04-24 12:19:54 +02:00
|
|
|
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2017-04-24 12:19:54 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_ldap_auth_only(self) -> None:
|
2017-10-24 20:44:01 +02:00
|
|
|
"""If the email auth backend is not enabled, password reset should do nothing"""
|
|
|
|
email = self.example_email("hamlet")
|
2020-12-23 21:45:16 +01:00
|
|
|
with self.assertLogs(level="INFO") as m:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email})
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
m.output,
|
|
|
|
[
|
|
|
|
"INFO:root:Password reset attempted for hamlet@zulip.com even though password auth is disabled."
|
|
|
|
],
|
|
|
|
)
|
2017-10-24 20:44:01 +02:00
|
|
|
|
|
|
|
# check the redirect link telling you to check mail for password reset link
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/"))
|
2017-10-24 20:44:01 +02:00
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
|
2018-09-16 02:34:57 +02:00
|
|
|
self.assert_in_response("Check your email in a few minutes to finish the process.", result)
|
2017-10-24 20:44:01 +02:00
|
|
|
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2017-10-24 20:44:01 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.EmailAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2018-05-29 07:09:48 +02:00
|
|
|
def test_ldap_and_email_auth(self) -> None:
|
2020-10-23 02:43:28 +02:00
|
|
|
"""If both email and LDAP auth backends are enabled, limit password
|
2021-02-12 08:19:30 +01:00
|
|
|
reset to users outside the LDAP domain"""
|
2018-05-29 07:09:48 +02:00
|
|
|
# If the domain matches, we don't generate an email
|
|
|
|
with self.settings(LDAP_APPEND_DOMAIN="zulip.com"):
|
|
|
|
email = self.example_email("hamlet")
|
2020-12-23 21:45:16 +01:00
|
|
|
with self.assertLogs(level="INFO") as m:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email})
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
m.output, ["INFO:root:Password reset not allowed for user in LDAP domain"]
|
|
|
|
)
|
2018-05-29 07:09:48 +02:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2018-05-29 07:09:48 +02:00
|
|
|
|
|
|
|
# If the domain doesn't match, we do generate an email
|
|
|
|
with self.settings(LDAP_APPEND_DOMAIN="example.com"):
|
|
|
|
email = self.example_email("hamlet")
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email})
|
2020-12-08 09:25:42 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/"))
|
2020-12-08 09:25:42 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2018-05-29 07:09:48 +02:00
|
|
|
|
2020-06-05 23:26:35 +02:00
|
|
|
body = self.get_reset_mail_body()
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIn("reset your password", body)
|
2018-05-29 07:09:48 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_redirect_endpoints(self) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
"""
|
2016-11-14 19:13:59 +01:00
|
|
|
These tests are mostly designed to give us 100% URL coverage
|
|
|
|
in our URL coverage reports. Our mechanism for finding URL
|
|
|
|
coverage doesn't handle redirects, so we just have a few quick
|
|
|
|
tests here.
|
2021-02-12 08:19:30 +01:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_get("/accounts/password/reset/done/")
|
2016-11-19 21:54:00 +01:00
|
|
|
self.assert_in_success_response(["Check your email"], result)
|
2016-11-14 19:13:59 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_get("/accounts/password/done/")
|
2023-02-27 19:37:44 +01:00
|
|
|
self.assert_in_success_response(["You've set a new password!"], result)
|
2016-11-14 19:13:59 +01:00
|
|
|
|
2023-01-20 08:22:48 +01:00
|
|
|
result = self.client_get("/accounts/send_confirm/?email=alice@example.com")
|
2018-08-24 10:22:11 +02:00
|
|
|
self.assert_in_success_response(["/accounts/home/"], result)
|
2016-11-14 19:13:59 +01:00
|
|
|
|
2023-01-20 08:22:48 +01:00
|
|
|
result = self.client_get("/accounts/new/send_confirm/?email=alice@example.com")
|
2018-08-24 10:22:11 +02:00
|
|
|
self.assert_in_success_response(["/new/"], result)
|
2018-08-24 10:01:42 +02:00
|
|
|
|
2022-05-25 17:27:40 +02:00
|
|
|
def test_password_reset_for_soft_deactivated_user(self) -> None:
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
email = user_profile.delivery_email
|
|
|
|
with self.soft_deactivate_and_check_long_term_idle(user_profile, False):
|
|
|
|
# start the password reset process by supplying an email address
|
|
|
|
result = self.client_post("/accounts/password/reset/", {"email": email})
|
|
|
|
|
|
|
|
# check the redirect link telling you to check mail for password reset link
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/"))
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-08-23 02:08:42 +02:00
|
|
|
class LoginTest(ZulipTestCase):
|
2014-01-31 21:08:40 +01:00
|
|
|
"""
|
|
|
|
Logging in, registration, and logging out.
|
|
|
|
"""
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_login(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
user_profile = self.example_user("hamlet")
|
2019-05-26 22:12:46 +02:00
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
2014-01-31 21:08:40 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_login_deactivated_user(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2021-03-27 06:02:12 +01:00
|
|
|
do_deactivate_user(user_profile, acting_user=None)
|
2021-08-23 15:14:05 +02:00
|
|
|
result = self.login_with_return(user_profile.delivery_email, "xxx")
|
2017-11-18 02:23:03 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2021-08-23 15:14:05 +02:00
|
|
|
self.assert_in_response(
|
2021-08-23 15:14:05 +02:00
|
|
|
f"Your account {user_profile.delivery_email} has been deactivated.", result
|
2021-08-23 15:14:05 +02:00
|
|
|
)
|
2019-05-26 22:12:46 +02:00
|
|
|
self.assert_logged_in_user_id(None)
|
2017-11-18 02:23:03 +01:00
|
|
|
|
2022-10-11 22:15:55 +02:00
|
|
|
def test_login_deactivate_user_error(self) -> None:
|
|
|
|
"""
|
|
|
|
This is meant to test whether the error message signaled by the
|
|
|
|
is_deactivated is shown independently of whether the Email
|
|
|
|
backend is enabled.
|
|
|
|
"""
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
realm = user_profile.realm
|
|
|
|
self.assertTrue(email_auth_enabled(realm))
|
|
|
|
|
|
|
|
url = f"{realm.uri}/login/?" + urlencode({"is_deactivated": user_profile.delivery_email})
|
|
|
|
result = self.client_get(url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
self.assert_in_response(
|
|
|
|
f"Your account {user_profile.delivery_email} has been deactivated.", result
|
|
|
|
)
|
|
|
|
|
|
|
|
auth_dict = realm.authentication_methods_dict()
|
|
|
|
auth_dict["Email"] = False
|
|
|
|
do_set_realm_authentication_methods(realm, auth_dict, acting_user=None)
|
|
|
|
result = self.client_get(url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
self.assert_in_response(
|
|
|
|
f"Your account {user_profile.delivery_email} has been deactivated.", result
|
|
|
|
)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_login_bad_password(self) -> None:
|
2020-03-12 14:17:25 +01:00
|
|
|
user = self.example_user("hamlet")
|
2020-07-05 01:38:05 +02:00
|
|
|
password: Optional[str] = "wrongpassword"
|
2020-03-12 14:17:25 +01:00
|
|
|
result = self.login_with_return(user.delivery_email, password=password)
|
|
|
|
self.assert_in_success_response([user.delivery_email], result)
|
2019-05-26 22:12:46 +02:00
|
|
|
self.assert_logged_in_user_id(None)
|
2014-01-31 21:08:40 +01:00
|
|
|
|
2020-03-12 14:17:25 +01:00
|
|
|
# Parallel test to confirm that the right password works using the
|
|
|
|
# same login code, which verifies our failing test isn't broken
|
|
|
|
# for some other reason.
|
|
|
|
password = initial_password(user.delivery_email)
|
|
|
|
result = self.login_with_return(user.delivery_email, password=password)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assert_logged_in_user_id(user.id)
|
|
|
|
|
2019-12-30 02:21:51 +01:00
|
|
|
@override_settings(RATE_LIMITING_AUTHENTICATE=True)
|
|
|
|
def test_login_bad_password_rate_limiter(self) -> None:
|
|
|
|
user_profile = self.example_user("hamlet")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = user_profile.delivery_email
|
2021-02-12 08:20:45 +01:00
|
|
|
add_ratelimit_rule(10, 2, domain="authenticate_by_username")
|
2019-12-30 02:21:51 +01:00
|
|
|
|
|
|
|
start_time = time.time()
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("time.time", return_value=start_time):
|
2019-12-30 02:21:51 +01:00
|
|
|
self.login_with_return(email, password="wrongpassword")
|
|
|
|
self.assert_logged_in_user_id(None)
|
|
|
|
self.login_with_return(email, password="wrongpassword")
|
|
|
|
self.assert_logged_in_user_id(None)
|
|
|
|
|
|
|
|
# We're over the allowed limit, so the next attempt, even with the correct
|
|
|
|
# password, will get blocked.
|
|
|
|
result = self.login_with_return(email)
|
|
|
|
self.assert_in_success_response(["Try again in 10 seconds"], result)
|
|
|
|
|
|
|
|
# After time passes, we should be able to log in.
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("time.time", return_value=start_time + 11):
|
2019-12-30 02:21:51 +01:00
|
|
|
self.login_with_return(email)
|
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
remove_ratelimit_rule(10, 2, domain="authenticate_by_username")
|
2019-12-30 02:21:51 +01:00
|
|
|
|
2021-05-07 15:10:35 +02:00
|
|
|
def test_login_with_old_weak_password_after_hasher_change(self) -> None:
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
password = "a_password_of_22_chars"
|
|
|
|
|
2021-05-07 15:14:24 +02:00
|
|
|
with self.settings(PASSWORD_HASHERS=("django.contrib.auth.hashers.SHA1PasswordHasher",)):
|
2021-05-07 15:10:35 +02:00
|
|
|
user_profile.set_password(password)
|
|
|
|
user_profile.save()
|
|
|
|
|
|
|
|
with self.settings(
|
|
|
|
PASSWORD_HASHERS=(
|
2021-05-07 15:14:24 +02:00
|
|
|
"django.contrib.auth.hashers.MD5PasswordHasher",
|
|
|
|
"django.contrib.auth.hashers.SHA1PasswordHasher",
|
2021-05-07 15:10:35 +02:00
|
|
|
),
|
|
|
|
PASSWORD_MIN_LENGTH=30,
|
|
|
|
), self.assertLogs("zulip.auth.email", level="INFO"):
|
|
|
|
result = self.login_with_return(self.example_email("hamlet"), password)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
self.assert_in_response(
|
2023-03-09 02:27:46 +01:00
|
|
|
"Your password has been disabled because it is too weak.", result
|
|
|
|
)
|
|
|
|
self.assert_logged_in_user_id(None)
|
2020-04-30 21:41:21 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_login_nonexistent_user(self) -> None:
|
|
|
|
result = self.login_with_return("xxx@zulip.com", "xxx")
|
2020-04-30 21:41:21 +02:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2023-03-09 02:27:46 +01:00
|
|
|
self.assert_in_response("Please enter a correct email and password", result)
|
|
|
|
self.assert_logged_in_user_id(None)
|
2020-04-30 21:41:21 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_login_wrong_subdomain(self) -> None:
|
|
|
|
user_profile = self.mit_user("sipbtest")
|
|
|
|
email = user_profile.delivery_email
|
|
|
|
with self.assertLogs("zulip.auth.OurAuthenticationForm", level="INFO") as m:
|
|
|
|
result = self.login_with_return(email, "xxx")
|
|
|
|
matching_accounts_dict = {"realm_id": user_profile.realm_id, "id": user_profile.id}
|
|
|
|
self.assertEqual(
|
|
|
|
m.output,
|
|
|
|
[
|
|
|
|
f"INFO:zulip.auth.OurAuthenticationForm:User attempted password login to wrong subdomain zulip. Matching accounts: [{matching_accounts_dict}]"
|
|
|
|
],
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2023-03-09 02:27:46 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
expected_error = (
|
|
|
|
"Please enter a correct email and password. Note that both fields may be case-sensitive"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2023-03-09 02:27:46 +01:00
|
|
|
self.assert_in_response(expected_error, result)
|
|
|
|
self.assert_logged_in_user_id(None)
|
2020-06-18 13:03:06 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_login_invalid_subdomain(self) -> None:
|
|
|
|
result = self.login_with_return(self.example_email("hamlet"), "xxx", subdomain="invalid")
|
|
|
|
self.assertEqual(result.status_code, 404)
|
|
|
|
self.assert_in_response("There is no Zulip organization hosted at this subdomain.", result)
|
|
|
|
self.assert_logged_in_user_id(None)
|
2020-06-18 13:03:06 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_register(self) -> None:
|
|
|
|
reset_email_visibility_to_everyone_in_zulip_realm()
|
2020-06-18 13:03:06 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
stream_names = [f"stream_{i}" for i in range(40)]
|
|
|
|
for stream_name in stream_names:
|
|
|
|
stream = self.make_stream(stream_name, realm=realm)
|
|
|
|
DefaultStream.objects.create(stream=stream, realm=realm)
|
2020-06-18 13:03:06 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
# Make sure there's at least one recent message to be mark
|
|
|
|
# unread. This prevents a bug where this test would start
|
|
|
|
# failing the test database was generated more than
|
|
|
|
# ONBOARDING_RECENT_TIMEDELTA ago.
|
|
|
|
self.subscribe(hamlet, "stream_0")
|
|
|
|
self.send_stream_message(
|
|
|
|
hamlet,
|
|
|
|
"stream_0",
|
|
|
|
topic_name="test topic",
|
|
|
|
content="test message",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-06-18 13:03:06 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
# Clear all the caches.
|
|
|
|
flush_per_request_caches()
|
|
|
|
ContentType.objects.clear_cache()
|
2021-11-30 13:34:37 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
# Ensure the number of queries we make is not O(streams)
|
2023-03-03 14:57:40 +01:00
|
|
|
with self.assert_database_query_count(94), cache_tries_captured() as cache_tries:
|
2023-03-09 02:27:46 +01:00
|
|
|
with self.captureOnCommitCallbacks(execute=True):
|
|
|
|
self.register(self.nonreg_email("test"), "test")
|
2021-11-30 13:34:37 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
# We can probably avoid a couple cache hits here, but there doesn't
|
|
|
|
# seem to be any O(N) behavior. Some of the cache hits are related
|
|
|
|
# to sending messages, such as getting the welcome bot, looking up
|
|
|
|
# the alert words for a realm, etc.
|
|
|
|
self.assert_length(cache_tries, 20)
|
2021-11-30 13:34:37 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
user_profile = self.nonreg_user("test")
|
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
|
|
|
self.assertFalse(user_profile.enable_stream_desktop_notifications)
|
|
|
|
self.check_user_added_in_system_group(user_profile)
|
2021-11-30 13:34:37 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_register_deactivated(self) -> None:
|
|
|
|
"""
|
|
|
|
If you try to register for a deactivated realm, you get a clear error
|
|
|
|
page.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2023-03-09 02:27:46 +01:00
|
|
|
realm.deactivated = True
|
|
|
|
realm.save(update_fields=["deactivated"])
|
2020-02-15 07:06:48 +01:00
|
|
|
|
|
|
|
result = self.client_post(
|
2023-03-09 02:27:46 +01:00
|
|
|
"/accounts/home/", {"email": self.nonreg_email("test")}, subdomain="zulip"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-02-15 07:06:48 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-03-09 02:27:46 +01:00
|
|
|
self.assertEqual("/accounts/deactivated/", result["Location"])
|
2017-08-10 22:34:17 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
with self.assertRaises(UserProfile.DoesNotExist):
|
|
|
|
self.nonreg_user("test")
|
2017-08-10 22:34:17 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_register_with_invalid_email(self) -> None:
|
|
|
|
"""
|
|
|
|
If you try to register with invalid email, you get an invalid email
|
|
|
|
page
|
|
|
|
"""
|
|
|
|
invalid_email = "foo\x00bar"
|
|
|
|
result = self.client_post("/accounts/home/", {"email": invalid_email}, subdomain="zulip")
|
2017-08-10 22:34:17 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 200)
|
2023-03-09 02:27:46 +01:00
|
|
|
self.assertContains(result, "Enter a valid email address")
|
2017-08-10 22:34:17 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_register_deactivated_partway_through(self) -> None:
|
|
|
|
"""
|
|
|
|
If you try to register for a deactivated realm, you get a clear error
|
|
|
|
page.
|
|
|
|
"""
|
|
|
|
email = self.nonreg_email("test")
|
|
|
|
result = self.client_post("/accounts/home/", {"email": email}, subdomain="zulip")
|
2017-08-10 22:34:17 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-03-09 02:27:46 +01:00
|
|
|
self.assertNotIn("deactivated", result["Location"])
|
2017-08-10 22:34:17 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.deactivated = True
|
|
|
|
realm.save(update_fields=["deactivated"])
|
2017-08-10 22:34:17 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
result = self.submit_reg_form_for_user(email, "abcd1234", subdomain="zulip")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual("/accounts/deactivated/", result["Location"])
|
2017-08-10 22:34:17 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
with self.assertRaises(UserProfile.DoesNotExist):
|
|
|
|
self.nonreg_user("test")
|
2017-08-10 22:34:17 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_login_deactivated_realm(self) -> None:
|
2022-01-23 13:22:12 +01:00
|
|
|
"""
|
2023-03-09 02:27:46 +01:00
|
|
|
If you try to log in to a deactivated realm, you get a clear error page.
|
2022-01-23 13:22:12 +01:00
|
|
|
"""
|
2023-03-09 02:27:46 +01:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.deactivated = True
|
|
|
|
realm.save(update_fields=["deactivated"])
|
2022-01-23 13:22:12 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
result = self.login_with_return(self.example_email("hamlet"), subdomain="zulip")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual("/accounts/deactivated/", result["Location"])
|
2022-01-23 13:22:12 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_logout(self) -> None:
|
|
|
|
self.login("hamlet")
|
|
|
|
# We use the logout API, not self.logout, to make sure we test
|
|
|
|
# the actual logout code path.
|
|
|
|
self.client_post("/accounts/logout/")
|
|
|
|
self.assert_logged_in_user_id(None)
|
2022-01-23 13:22:12 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_non_ascii_login(self) -> None:
|
|
|
|
"""
|
|
|
|
You can log in even if your password contain non-ASCII characters.
|
|
|
|
"""
|
|
|
|
email = self.nonreg_email("test")
|
|
|
|
password = "hümbüǵ"
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
# Registering succeeds.
|
|
|
|
self.register(email, password)
|
|
|
|
user_profile = self.nonreg_user("test")
|
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
|
|
|
self.logout()
|
|
|
|
self.assert_logged_in_user_id(None)
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
# Logging in succeeds.
|
|
|
|
self.logout()
|
|
|
|
self.login_by_email(email, password)
|
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
@override_settings(TWO_FACTOR_AUTHENTICATION_ENABLED=False)
|
|
|
|
def test_login_page_redirects_logged_in_user(self) -> None:
|
|
|
|
"""You will be redirected to the app's main page if you land on the
|
|
|
|
login page when already logged in.
|
|
|
|
"""
|
|
|
|
self.login("cordelia")
|
|
|
|
response = self.client_get("/login/")
|
|
|
|
self.assertEqual(response["Location"], "http://zulip.testserver")
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_options_request_to_login_page(self) -> None:
|
|
|
|
response = self.client_options("/login/")
|
|
|
|
self.assertEqual(response.status_code, 200)
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
@override_settings(TWO_FACTOR_AUTHENTICATION_ENABLED=True)
|
|
|
|
def test_login_page_redirects_logged_in_user_under_2fa(self) -> None:
|
|
|
|
"""You will be redirected to the app's main page if you land on the
|
|
|
|
login page when already logged in.
|
|
|
|
"""
|
|
|
|
user_profile = self.example_user("cordelia")
|
|
|
|
self.create_default_device(user_profile)
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
self.login("cordelia")
|
|
|
|
self.login_2fa(user_profile)
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
response = self.client_get("/login/")
|
|
|
|
self.assertEqual(response["Location"], "http://zulip.testserver")
|
2020-06-18 13:03:06 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_start_two_factor_auth(self) -> None:
|
|
|
|
request = HostRequestMock()
|
|
|
|
with patch("zerver.views.auth.TwoFactorLoginView") as mock_view:
|
|
|
|
mock_view.as_view.return_value = lambda *a, **k: HttpResponse()
|
|
|
|
response = start_two_factor_auth(request)
|
|
|
|
self.assertTrue(isinstance(response, HttpResponse))
|
2020-06-18 13:03:06 +02:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_do_two_factor_login(self) -> None:
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
self.create_default_device(user_profile)
|
|
|
|
request = HostRequestMock()
|
|
|
|
with patch("zerver.decorator.django_otp.login") as mock_login:
|
|
|
|
do_two_factor_login(request, user_profile)
|
|
|
|
mock_login.assert_called_once()
|
2018-03-02 12:27:57 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
def test_zulip_default_context_does_not_load_inline_previews(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
description = "https://www.google.com/images/srpr/logo4w.png"
|
|
|
|
realm.description = description
|
|
|
|
realm.save(update_fields=["description"])
|
|
|
|
response: HttpResponseBase = self.client_get("/login/")
|
|
|
|
expected_response = """<p><a href="https://www.google.com/images/srpr/logo4w.png">\
|
|
|
|
https://www.google.com/images/srpr/logo4w.png</a></p>"""
|
|
|
|
assert isinstance(response, TemplateResponse)
|
|
|
|
assert response.context_data is not None
|
|
|
|
self.assertEqual(response.context_data["realm_description"], expected_response)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
2022-12-28 00:23:47 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-08-23 02:08:42 +02:00
|
|
|
class EmailUnsubscribeTests(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_error_unsubscribe(self) -> None:
|
2017-11-07 20:29:37 +01:00
|
|
|
# An invalid unsubscribe token "test123" produces an error.
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_get("/accounts/unsubscribe/missed_messages/test123")
|
|
|
|
self.assert_in_response("Unknown email unsubscribe request", result)
|
2016-12-01 08:54:21 +01:00
|
|
|
|
2017-02-26 21:48:38 +01:00
|
|
|
# An unknown message type "fake" produces an error.
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2017-02-26 21:48:38 +01:00
|
|
|
unsubscribe_link = one_click_unsubscribe_link(user_profile, "fake")
|
|
|
|
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_response("Unknown email unsubscribe request", result)
|
2017-02-26 21:48:38 +01:00
|
|
|
|
2021-04-20 23:27:25 +02:00
|
|
|
def test_message_notification_emails_unsubscribe(self) -> None:
|
2014-01-31 21:08:40 +01:00
|
|
|
"""
|
2021-04-20 23:27:25 +02:00
|
|
|
We provide one-click unsubscribe links in message notification emails
|
|
|
|
that you can click even when logged out to update your
|
2014-01-31 21:08:40 +01:00
|
|
|
email notification settings.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2014-01-31 21:08:40 +01:00
|
|
|
user_profile.enable_offline_email_notifications = True
|
|
|
|
user_profile.save()
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
unsubscribe_link = one_click_unsubscribe_link(user_profile, "missed_messages")
|
2016-07-28 00:38:45 +02:00
|
|
|
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path)
|
2014-01-31 21:08:40 +01:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 200)
|
2017-08-15 21:52:23 +02:00
|
|
|
|
|
|
|
user_profile.refresh_from_db()
|
2014-01-31 21:08:40 +01:00
|
|
|
self.assertFalse(user_profile.enable_offline_email_notifications)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_welcome_unsubscribe(self) -> None:
|
2014-01-31 21:08:40 +01:00
|
|
|
"""
|
|
|
|
We provide one-click unsubscribe links in welcome e-mails that you can
|
|
|
|
click even when logged out to stop receiving them.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2014-01-31 21:08:40 +01:00
|
|
|
# Simulate a new user signing up, which enqueues 2 welcome e-mails.
|
2017-09-22 04:29:01 +02:00
|
|
|
enqueue_welcome_emails(user_profile)
|
2019-01-04 01:50:21 +01:00
|
|
|
self.assertEqual(2, ScheduledEmail.objects.filter(users=user_profile).count())
|
2014-01-31 21:08:40 +01:00
|
|
|
|
|
|
|
# Simulate unsubscribing from the welcome e-mails.
|
|
|
|
unsubscribe_link = one_click_unsubscribe_link(user_profile, "welcome")
|
2016-07-28 00:38:45 +02:00
|
|
|
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path)
|
2014-01-31 21:08:40 +01:00
|
|
|
|
|
|
|
# The welcome email jobs are no longer scheduled.
|
|
|
|
self.assertEqual(result.status_code, 200)
|
2019-01-04 01:50:21 +01:00
|
|
|
self.assertEqual(0, ScheduledEmail.objects.filter(users=user_profile).count())
|
2014-01-31 21:08:40 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_digest_unsubscribe(self) -> None:
|
2014-01-31 21:08:40 +01:00
|
|
|
"""
|
|
|
|
We provide one-click unsubscribe links in digest e-mails that you can
|
|
|
|
click even when logged out to stop receiving them.
|
|
|
|
|
|
|
|
Unsubscribing from these emails also dequeues any digest email jobs that
|
|
|
|
have been queued.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2014-01-31 21:08:40 +01:00
|
|
|
self.assertTrue(user_profile.enable_digest_emails)
|
|
|
|
|
|
|
|
# Enqueue a fake digest email.
|
2021-02-12 08:19:30 +01:00
|
|
|
context = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"name": "",
|
|
|
|
"realm_uri": "",
|
|
|
|
"unread_pms": [],
|
|
|
|
"hot_conversations": [],
|
|
|
|
"new_users": [],
|
|
|
|
"new_streams": {"plain": []},
|
|
|
|
"unsubscribe_link": "",
|
2021-02-12 08:19:30 +01:00
|
|
|
}
|
|
|
|
send_future_email(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zerver/emails/digest",
|
2021-02-12 08:19:30 +01:00
|
|
|
user_profile.realm,
|
|
|
|
to_user_ids=[user_profile.id],
|
|
|
|
context=context,
|
|
|
|
)
|
2017-05-02 02:07:01 +02:00
|
|
|
|
2019-01-04 01:50:21 +01:00
|
|
|
self.assertEqual(1, ScheduledEmail.objects.filter(users=user_profile).count())
|
2014-01-31 21:08:40 +01:00
|
|
|
|
|
|
|
# Simulate unsubscribing from digest e-mails.
|
|
|
|
unsubscribe_link = one_click_unsubscribe_link(user_profile, "digest")
|
2016-07-28 00:38:45 +02:00
|
|
|
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path)
|
2014-01-31 21:08:40 +01:00
|
|
|
|
|
|
|
# The setting is toggled off, and scheduled jobs have been removed.
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
# Circumvent user_profile caching.
|
2017-08-15 21:52:23 +02:00
|
|
|
|
|
|
|
user_profile.refresh_from_db()
|
2014-01-31 21:08:40 +01:00
|
|
|
self.assertFalse(user_profile.enable_digest_emails)
|
2019-01-04 01:50:21 +01:00
|
|
|
self.assertEqual(0, ScheduledEmail.objects.filter(users=user_profile).count())
|
2014-01-31 21:08:40 +01:00
|
|
|
|
2018-11-07 16:54:23 +01:00
|
|
|
def test_login_unsubscribe(self) -> None:
|
|
|
|
"""
|
|
|
|
We provide one-click unsubscribe links in login
|
|
|
|
e-mails that you can click even when logged out to update your
|
|
|
|
email notification settings.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2018-11-07 16:54:23 +01:00
|
|
|
user_profile.enable_login_emails = True
|
|
|
|
user_profile.save()
|
|
|
|
|
|
|
|
unsubscribe_link = one_click_unsubscribe_link(user_profile, "login")
|
|
|
|
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path)
|
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
user_profile.refresh_from_db()
|
|
|
|
self.assertFalse(user_profile.enable_login_emails)
|
|
|
|
|
2021-04-28 00:25:27 +02:00
|
|
|
def test_marketing_unsubscribe(self) -> None:
|
|
|
|
"""
|
|
|
|
We provide one-click unsubscribe links in marketing e-mails that you can
|
|
|
|
click even when logged out to stop receiving them.
|
|
|
|
"""
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
self.assertTrue(user_profile.enable_marketing_emails)
|
|
|
|
|
2021-04-29 01:12:28 +02:00
|
|
|
# Simulate unsubscribing from marketing e-mails.
|
2021-04-28 00:25:27 +02:00
|
|
|
unsubscribe_link = one_click_unsubscribe_link(user_profile, "marketing")
|
|
|
|
result = self.client_get(urllib.parse.urlparse(unsubscribe_link).path)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
# Circumvent user_profile caching.
|
|
|
|
user_profile.refresh_from_db()
|
|
|
|
self.assertFalse(user_profile.enable_marketing_emails)
|
|
|
|
|
2021-04-29 00:34:05 +02:00
|
|
|
def test_marketing_unsubscribe_post(self) -> None:
|
|
|
|
"""
|
|
|
|
The List-Unsubscribe-Post header lets email clients trigger an
|
|
|
|
automatic unsubscription request via POST (see RFC 8058), so
|
|
|
|
test that too.
|
|
|
|
"""
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
|
|
self.assertTrue(user_profile.enable_marketing_emails)
|
|
|
|
|
|
|
|
# Simulate unsubscribing from marketing e-mails.
|
|
|
|
unsubscribe_link = one_click_unsubscribe_link(user_profile, "marketing")
|
|
|
|
client = Client(enforce_csrf_checks=True)
|
|
|
|
result = client.post(
|
|
|
|
urllib.parse.urlparse(unsubscribe_link).path, {"List-Unsubscribe": "One-Click"}
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
# Circumvent user_profile caching.
|
|
|
|
user_profile.refresh_from_db()
|
|
|
|
self.assertFalse(user_profile.enable_marketing_emails)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-08-23 02:08:42 +02:00
|
|
|
class RealmCreationTest(ZulipTestCase):
|
2017-11-25 00:00:26 +01:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
2021-02-12 08:19:30 +01:00
|
|
|
def check_able_to_create_realm(self, email: str, password: str = "test") -> None:
|
2021-03-08 11:54:39 +01:00
|
|
|
internal_realm = get_realm(settings.SYSTEM_BOT_REALM)
|
|
|
|
notification_bot = get_system_bot(settings.NOTIFICATION_BOT, internal_realm.id)
|
2021-02-12 08:20:45 +01:00
|
|
|
signups_stream, _ = create_stream_if_needed(notification_bot.realm, "signups")
|
2020-02-10 17:37:57 +01:00
|
|
|
|
2016-10-31 23:28:20 +01:00
|
|
|
string_id = "zuliptest"
|
2023-03-10 16:03:18 +01:00
|
|
|
org_name = "Zulip Test"
|
2016-06-03 01:02:58 +02:00
|
|
|
# Make sure the realm does not exist
|
2019-05-04 04:47:44 +02:00
|
|
|
with self.assertRaises(Realm.DoesNotExist):
|
|
|
|
get_realm(string_id)
|
2016-06-03 01:02:58 +02:00
|
|
|
|
2017-11-25 00:00:26 +01:00
|
|
|
# Create new realm with the email
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name=org_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2023-03-21 14:11:44 +01:00
|
|
|
|
2017-11-25 00:00:26 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-11-25 00:00:26 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2023-03-03 11:58:00 +01:00
|
|
|
prereg_realm = PreregistrationRealm.objects.get(email=email)
|
|
|
|
self.assertEqual(prereg_realm.name, "Zulip Test")
|
|
|
|
self.assertEqual(prereg_realm.org_type, Realm.ORG_TYPES["business"]["id"])
|
|
|
|
self.assertEqual(prereg_realm.string_id, string_id)
|
2016-06-03 01:02:58 +02:00
|
|
|
|
2021-05-26 19:40:12 +02:00
|
|
|
# Check confirmation email has the correct subject and body, extract
|
|
|
|
# confirmation link and visit it
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(
|
|
|
|
email,
|
|
|
|
email_subject_contains="Create your Zulip organization",
|
|
|
|
email_body_contains="You have requested a new Zulip organization",
|
|
|
|
)
|
2017-11-25 00:00:26 +01:00
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2023-03-10 16:03:18 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email, password, realm_subdomain=string_id, realm_name=org_name
|
|
|
|
)
|
2017-11-25 00:00:26 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
2021-02-12 08:20:45 +01:00
|
|
|
result["Location"].startswith("http://zuliptest.testserver/accounts/login/subdomain/")
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-11-25 00:00:26 +01:00
|
|
|
|
|
|
|
# Make sure the realm is created
|
|
|
|
realm = get_realm(string_id)
|
|
|
|
self.assertEqual(realm.string_id, string_id)
|
2019-11-07 05:11:51 +01:00
|
|
|
user = get_user(email, realm)
|
|
|
|
self.assertEqual(user.realm, realm)
|
|
|
|
|
2020-05-16 20:50:57 +02:00
|
|
|
# Check that user is the owner.
|
|
|
|
self.assertEqual(user.role, UserProfile.ROLE_REALM_OWNER)
|
2017-11-25 00:00:26 +01:00
|
|
|
|
|
|
|
# Check defaults
|
2021-06-24 20:05:06 +02:00
|
|
|
self.assertEqual(realm.org_type, Realm.ORG_TYPES["business"]["id"])
|
2018-07-27 23:26:29 +02:00
|
|
|
self.assertEqual(realm.emails_restricted_to_domains, False)
|
2017-11-25 00:00:26 +01:00
|
|
|
self.assertEqual(realm.invite_required, True)
|
|
|
|
|
2023-03-10 11:42:18 +01:00
|
|
|
prereg_realm = PreregistrationRealm.objects.get(email=email)
|
|
|
|
# Check created_realm and created_user field of PreregistrationRealm object
|
|
|
|
self.assertEqual(prereg_realm.created_realm, realm)
|
|
|
|
self.assertEqual(prereg_realm.created_user, user)
|
|
|
|
self.assertEqual(prereg_realm.status, confirmation_settings.STATUS_USED)
|
|
|
|
|
2017-11-25 00:00:26 +01:00
|
|
|
# Check welcome messages
|
|
|
|
for stream_name, text, message_count in [
|
2021-02-12 08:20:45 +01:00
|
|
|
(Realm.DEFAULT_NOTIFICATION_STREAM_NAME, "with the topic", 3),
|
|
|
|
(Realm.INITIAL_PRIVATE_STREAM_NAME, "private stream", 1),
|
2021-02-12 08:19:30 +01:00
|
|
|
]:
|
2017-11-25 00:00:26 +01:00
|
|
|
stream = get_stream(stream_name, realm)
|
2020-02-18 17:25:43 +01:00
|
|
|
recipient = stream.recipient
|
2021-02-12 08:20:45 +01:00
|
|
|
messages = Message.objects.filter(recipient=recipient).order_by("date_sent")
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(messages, message_count)
|
2017-11-25 00:00:26 +01:00
|
|
|
self.assertIn(text, messages[0].content)
|
|
|
|
|
2023-03-10 16:03:18 +01:00
|
|
|
# Check admin organization's signups stream messages
|
2020-02-18 17:25:43 +01:00
|
|
|
recipient = signups_stream.recipient
|
2021-02-12 08:20:45 +01:00
|
|
|
messages = Message.objects.filter(recipient=recipient).order_by("id")
|
2023-03-13 12:09:08 +01:00
|
|
|
self.assert_length(messages, 1)
|
2023-03-10 16:03:18 +01:00
|
|
|
# Check organization name, subdomain and organization type are in message content
|
|
|
|
self.assertIn("Zulip Test", messages[0].content)
|
|
|
|
self.assertIn("zuliptest", messages[0].content)
|
|
|
|
self.assertIn("Organization type: Business", messages[0].content)
|
|
|
|
self.assertEqual("new organizations", messages[0].topic_name())
|
2020-02-10 17:37:57 +01:00
|
|
|
|
2021-04-20 12:29:19 +02:00
|
|
|
realm_creation_audit_log = RealmAuditLog.objects.get(
|
|
|
|
realm=realm, event_type=RealmAuditLog.REALM_CREATED
|
|
|
|
)
|
|
|
|
self.assertEqual(realm_creation_audit_log.acting_user, user)
|
|
|
|
self.assertEqual(realm_creation_audit_log.event_time, realm.date_created)
|
|
|
|
|
2020-02-10 17:37:57 +01:00
|
|
|
# Piggyback a little check for how we handle
|
|
|
|
# empty string_ids.
|
2021-02-12 08:20:45 +01:00
|
|
|
realm.string_id = ""
|
|
|
|
self.assertEqual(realm.display_subdomain, ".")
|
2020-02-10 17:37:57 +01:00
|
|
|
|
2017-11-25 00:00:26 +01:00
|
|
|
def test_create_realm_non_existing_email(self) -> None:
|
|
|
|
self.check_able_to_create_realm("user1@test.com")
|
2017-05-08 20:25:03 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_create_realm_existing_email(self) -> None:
|
2017-11-22 20:22:11 +01:00
|
|
|
self.check_able_to_create_realm("hamlet@zulip.com")
|
2017-11-22 20:05:53 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=("zproject.backends.ZulipLDAPAuthBackend",))
|
2019-11-07 05:13:08 +01:00
|
|
|
def test_create_realm_ldap_email(self) -> None:
|
|
|
|
self.init_default_ldap_database()
|
|
|
|
|
|
|
|
with self.settings(LDAP_EMAIL_ATTR="mail"):
|
2021-02-12 08:19:30 +01:00
|
|
|
self.check_able_to_create_realm(
|
|
|
|
"newuser_email@zulip.com", self.ldap_password("newuser_with_email")
|
|
|
|
)
|
2019-11-07 05:13:08 +01:00
|
|
|
|
2017-11-22 20:05:53 +01:00
|
|
|
def test_create_realm_as_system_bot(self) -> None:
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email="notification-bot@zulip.com", realm_subdomain="zuliptest", realm_name="Zulip test"
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2017-11-22 20:05:53 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_response("notification-bot@zulip.com is reserved for system bots", result)
|
2017-03-11 20:12:01 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_create_realm_no_creation_key(self) -> None:
|
2017-03-11 20:12:01 +01:00
|
|
|
"""
|
|
|
|
Trying to create a realm without a creation_key should fail when
|
|
|
|
OPEN_REALM_CREATION is false.
|
|
|
|
"""
|
|
|
|
email = "user1@test.com"
|
|
|
|
|
|
|
|
with self.settings(OPEN_REALM_CREATION=False):
|
|
|
|
# Create new realm with the email, but no creation key.
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain="zuliptest", realm_name="Zulip test"
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2017-03-11 20:12:01 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2022-10-20 22:15:49 +02:00
|
|
|
self.assert_in_response("Organization creation link required", result)
|
2017-03-11 20:12:01 +01:00
|
|
|
|
2017-10-02 22:43:43 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_create_realm_with_subdomain(self) -> None:
|
2016-07-19 14:35:08 +02:00
|
|
|
password = "test"
|
2016-10-31 23:28:20 +01:00
|
|
|
string_id = "zuliptest"
|
2016-07-19 14:35:08 +02:00
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
# Make sure the realm does not exist
|
2019-05-04 04:47:44 +02:00
|
|
|
with self.assertRaises(Realm.DoesNotExist):
|
|
|
|
get_realm(string_id)
|
2016-11-04 01:33:46 +01:00
|
|
|
|
2017-10-02 22:43:43 +02:00
|
|
|
# Create new realm with the email
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name=realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2017-10-02 22:43:43 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-10-02 22:43:43 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2016-07-19 14:35:08 +02:00
|
|
|
|
2017-10-02 22:43:43 +02:00
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
2016-07-19 14:35:08 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email, password, realm_subdomain=string_id, realm_name=realm_name
|
|
|
|
)
|
2017-10-02 22:43:43 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2016-07-19 14:35:08 +02:00
|
|
|
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"], subdomain=string_id)
|
2020-05-22 15:42:46 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "http://zuliptest.testserver")
|
2020-05-22 15:42:46 +02:00
|
|
|
|
2017-10-02 22:43:43 +02:00
|
|
|
# Make sure the realm is created
|
|
|
|
realm = get_realm(string_id)
|
|
|
|
self.assertEqual(realm.string_id, string_id)
|
|
|
|
self.assertEqual(get_user(email, realm).realm, realm)
|
2016-10-13 20:09:32 +02:00
|
|
|
|
2017-10-02 22:43:43 +02:00
|
|
|
self.assertEqual(realm.name, realm_name)
|
|
|
|
self.assertEqual(realm.subdomain, string_id)
|
2016-11-04 01:33:46 +01:00
|
|
|
|
2021-08-16 21:03:33 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
|
|
|
def test_create_realm_with_marketing_emails_enabled(self) -> None:
|
|
|
|
password = "test"
|
|
|
|
string_id = "zuliptest"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
# Make sure the realm does not exist
|
|
|
|
with self.assertRaises(Realm.DoesNotExist):
|
|
|
|
get_realm(string_id)
|
|
|
|
|
|
|
|
# Create new realm with the email
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name=realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2021-08-16 21:03:33 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2021-08-16 21:03:33 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2021-08-16 21:03:33 +02:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
realm_subdomain=string_id,
|
|
|
|
realm_name=realm_name,
|
|
|
|
enable_marketing_emails=True,
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"], subdomain=string_id)
|
2021-08-16 21:03:33 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "http://zuliptest.testserver")
|
2021-08-16 21:03:33 +02:00
|
|
|
|
|
|
|
# Make sure the realm is created
|
|
|
|
realm = get_realm(string_id)
|
|
|
|
self.assertEqual(realm.string_id, string_id)
|
|
|
|
user = get_user(email, realm)
|
|
|
|
self.assertEqual(user.realm, realm)
|
|
|
|
self.assertTrue(user.enable_marketing_emails)
|
|
|
|
|
2022-01-05 23:53:40 +01:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True, CORPORATE_ENABLED=False)
|
|
|
|
def test_create_realm_without_prompting_for_marketing_emails(self) -> None:
|
|
|
|
password = "test"
|
|
|
|
string_id = "zuliptest"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
# Make sure the realm does not exist
|
|
|
|
with self.assertRaises(Realm.DoesNotExist):
|
|
|
|
get_realm(string_id)
|
|
|
|
|
|
|
|
# Create new realm with the email
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name=realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2022-01-05 23:53:40 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2022-01-05 23:53:40 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2022-01-05 23:53:40 +01:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
# Simulate the initial POST that is made by confirm-preregistration.js
|
|
|
|
# by triggering submit on confirm_preregistration.html.
|
|
|
|
payload = {
|
|
|
|
"full_name": "",
|
|
|
|
"key": find_key_by_email(email),
|
|
|
|
"from_confirmation": "1",
|
|
|
|
}
|
2023-02-24 09:08:26 +01:00
|
|
|
result = self.client_post("/realm/register/", payload)
|
2022-01-05 23:53:40 +01:00
|
|
|
# Assert that the form did not prompt the user for enabling
|
|
|
|
# marketing emails.
|
|
|
|
self.assert_not_in_success_response(['input id="id_enable_marketing_emails"'], result)
|
|
|
|
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
realm_subdomain=string_id,
|
|
|
|
realm_name=realm_name,
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"], subdomain=string_id)
|
2022-01-05 23:53:40 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "http://zuliptest.testserver")
|
2022-01-05 23:53:40 +01:00
|
|
|
|
|
|
|
# Make sure the realm is created
|
|
|
|
realm = get_realm(string_id)
|
|
|
|
self.assertEqual(realm.string_id, string_id)
|
|
|
|
user = get_user(email, realm)
|
|
|
|
self.assertEqual(user.realm, realm)
|
|
|
|
self.assertFalse(user.enable_marketing_emails)
|
|
|
|
|
2021-08-16 21:03:33 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
|
|
|
def test_create_realm_with_marketing_emails_disabled(self) -> None:
|
|
|
|
password = "test"
|
|
|
|
string_id = "zuliptest"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
# Make sure the realm does not exist
|
|
|
|
with self.assertRaises(Realm.DoesNotExist):
|
|
|
|
get_realm(string_id)
|
|
|
|
|
|
|
|
# Create new realm with the email
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name="Zulip test"
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2021-08-16 21:03:33 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2021-08-16 21:03:33 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2021-08-16 21:03:33 +02:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
realm_subdomain=string_id,
|
|
|
|
realm_name=realm_name,
|
|
|
|
enable_marketing_emails=False,
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"], subdomain=string_id)
|
2021-08-16 21:03:33 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "http://zuliptest.testserver")
|
2021-08-16 21:03:33 +02:00
|
|
|
|
|
|
|
# Make sure the realm is created
|
|
|
|
realm = get_realm(string_id)
|
|
|
|
self.assertEqual(realm.string_id, string_id)
|
|
|
|
user = get_user(email, realm)
|
|
|
|
self.assertEqual(user.realm, realm)
|
|
|
|
self.assertFalse(user.enable_marketing_emails)
|
|
|
|
|
2021-09-09 06:50:40 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
|
|
|
def test_create_regular_realm_welcome_bot_pm(self) -> None:
|
|
|
|
password = "test"
|
|
|
|
string_id = "zuliptest"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
# Create new realm with the email
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name=realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2021-09-09 06:50:40 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2021-09-09 06:50:40 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2021-09-09 06:50:40 +02:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
realm_subdomain=string_id,
|
|
|
|
realm_name=realm_name,
|
|
|
|
enable_marketing_emails=False,
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
# Make sure the correct Welcome Bot PM is sent
|
|
|
|
welcome_msg = Message.objects.filter(
|
|
|
|
sender__email="welcome-bot@zulip.com", recipient__type=Recipient.PERSONAL
|
|
|
|
).latest("id")
|
|
|
|
self.assertTrue(welcome_msg.content.startswith("Hello, and welcome to Zulip!"))
|
|
|
|
self.assertNotIn("demo organization", welcome_msg.content)
|
|
|
|
|
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
|
|
|
def test_create_demo_realm_welcome_bot_pm(self) -> None:
|
|
|
|
password = "test"
|
|
|
|
string_id = "zuliptest"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
# Create new realm with the email
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name=realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2021-09-09 06:50:40 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2021-09-09 06:50:40 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2021-09-09 06:50:40 +02:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
realm_subdomain=string_id,
|
|
|
|
realm_name=realm_name,
|
|
|
|
enable_marketing_emails=False,
|
|
|
|
is_demo_organization=True,
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
# Make sure the correct Welcome Bot PM is sent
|
|
|
|
welcome_msg = Message.objects.filter(
|
|
|
|
sender__email="welcome-bot@zulip.com", recipient__type=Recipient.PERSONAL
|
|
|
|
).latest("id")
|
|
|
|
self.assertTrue(welcome_msg.content.startswith("Hello, and welcome to Zulip!"))
|
|
|
|
self.assertIn("demo organization", welcome_msg.content)
|
|
|
|
|
2020-05-22 15:42:46 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True, FREE_TRIAL_DAYS=30)
|
|
|
|
def test_create_realm_during_free_trial(self) -> None:
|
|
|
|
password = "test"
|
|
|
|
string_id = "zuliptest"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
with self.assertRaises(Realm.DoesNotExist):
|
|
|
|
get_realm(string_id)
|
|
|
|
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name=realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2023-03-21 14:11:44 +01:00
|
|
|
|
2020-05-22 15:42:46 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2020-05-22 15:42:46 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2020-05-22 15:42:46 +02:00
|
|
|
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email, password, realm_subdomain=string_id, realm_name=realm_name
|
|
|
|
)
|
2020-05-22 15:42:46 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"], subdomain=string_id)
|
|
|
|
self.assertEqual(result["Location"], "http://zuliptest.testserver/upgrade/?onboarding=true")
|
2020-05-22 15:42:46 +02:00
|
|
|
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"], subdomain=string_id)
|
2020-05-27 02:58:46 +02:00
|
|
|
self.assert_in_success_response(["Not ready to start your trial?"], result)
|
2020-05-22 15:42:46 +02:00
|
|
|
|
|
|
|
realm = get_realm(string_id)
|
|
|
|
self.assertEqual(realm.string_id, string_id)
|
|
|
|
self.assertEqual(get_user(email, realm).realm, realm)
|
|
|
|
|
|
|
|
self.assertEqual(realm.name, realm_name)
|
|
|
|
self.assertEqual(realm.subdomain, string_id)
|
|
|
|
|
2021-02-11 17:52:59 +01:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
|
|
|
def test_create_two_realms(self) -> None:
|
|
|
|
"""
|
2023-03-10 11:42:18 +01:00
|
|
|
Verify correct behavior and PreregistrationRealm handling when using
|
2021-02-11 17:52:59 +01:00
|
|
|
two pre-generated realm creation links to create two different realms.
|
|
|
|
"""
|
|
|
|
password = "test"
|
|
|
|
first_string_id = "zuliptest"
|
|
|
|
second_string_id = "zuliptest2"
|
|
|
|
email = "user1@test.com"
|
|
|
|
first_realm_name = "Test"
|
|
|
|
second_realm_name = "Test"
|
|
|
|
|
|
|
|
# Make sure the realms do not exist
|
|
|
|
with self.assertRaises(Realm.DoesNotExist):
|
|
|
|
get_realm(first_string_id)
|
|
|
|
with self.assertRaises(Realm.DoesNotExist):
|
|
|
|
get_realm(second_string_id)
|
|
|
|
|
|
|
|
# Now we pre-generate two realm creation links
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=first_string_id, realm_name=first_realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2021-02-11 17:52:59 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2021-02-11 17:52:59 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2021-02-11 17:52:59 +01:00
|
|
|
first_confirmation_url = self.get_confirmation_url_from_outbox(email)
|
2023-03-10 11:42:18 +01:00
|
|
|
self.assertEqual(PreregistrationRealm.objects.filter(email=email, status=0).count(), 1)
|
2021-02-11 17:52:59 +01:00
|
|
|
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=second_string_id, realm_name=second_realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2021-02-11 17:52:59 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2021-02-11 17:52:59 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2021-02-11 17:52:59 +01:00
|
|
|
second_confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
|
|
|
|
self.assertNotEqual(first_confirmation_url, second_confirmation_url)
|
2023-03-10 11:42:18 +01:00
|
|
|
self.assertEqual(PreregistrationRealm.objects.filter(email=email, status=0).count(), 2)
|
2021-02-11 17:52:59 +01:00
|
|
|
|
|
|
|
# Create and verify the first realm
|
|
|
|
result = self.client_get(first_confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
realm_subdomain=first_string_id,
|
|
|
|
realm_name=first_realm_name,
|
|
|
|
key=first_confirmation_url.split("/")[-1],
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
# Make sure the realm is created
|
|
|
|
realm = get_realm(first_string_id)
|
|
|
|
self.assertEqual(realm.string_id, first_string_id)
|
|
|
|
self.assertEqual(realm.name, first_realm_name)
|
|
|
|
|
2023-03-10 11:42:18 +01:00
|
|
|
# One of the PreregistrationRealm should have been used up:
|
|
|
|
self.assertEqual(PreregistrationRealm.objects.filter(email=email, status=0).count(), 1)
|
2021-02-11 17:52:59 +01:00
|
|
|
|
|
|
|
# Create and verify the second realm
|
|
|
|
result = self.client_get(second_confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
realm_subdomain=second_string_id,
|
|
|
|
realm_name=second_realm_name,
|
|
|
|
key=second_confirmation_url.split("/")[-1],
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
# Make sure the realm is created
|
|
|
|
realm = get_realm(second_string_id)
|
|
|
|
self.assertEqual(realm.string_id, second_string_id)
|
|
|
|
self.assertEqual(realm.name, second_realm_name)
|
|
|
|
|
2023-03-10 11:42:18 +01:00
|
|
|
# The remaining PreregistrationRealm should have been used up:
|
|
|
|
self.assertEqual(PreregistrationRealm.objects.filter(email=email, status=0).count(), 0)
|
2021-02-11 17:52:59 +01:00
|
|
|
|
2022-08-16 00:44:46 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
|
|
|
def test_invalid_email_signup(self) -> None:
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email="<foo", realm_subdomain="zuliptest", realm_name="Zulip test"
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2022-09-19 01:44:52 +02:00
|
|
|
self.assert_in_response("Please use your real email address.", result)
|
|
|
|
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email="foo\x00bar", realm_subdomain="zuliptest", realm_name="Zulip test"
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2022-08-16 00:44:46 +02:00
|
|
|
self.assert_in_response("Please use your real email address.", result)
|
|
|
|
|
2017-10-02 22:43:43 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_mailinator_signup(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/new/", {"email": "hi@mailinator.com"})
|
|
|
|
self.assert_in_response("Please use your real email address.", result)
|
2016-11-05 03:26:30 +01:00
|
|
|
|
2017-10-02 22:43:43 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_subdomain_restrictions(self) -> None:
|
2016-10-31 23:28:20 +01:00
|
|
|
password = "test"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
errors = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"id": "length 3 or greater",
|
|
|
|
"-id": "cannot start or end with a",
|
|
|
|
"string-ID": "lowercase letters",
|
|
|
|
"string_id": "lowercase letters",
|
|
|
|
"stream": "unavailable",
|
|
|
|
"streams": "unavailable",
|
|
|
|
"about": "unavailable",
|
|
|
|
"abouts": "unavailable",
|
|
|
|
"zephyr": "unavailable",
|
2021-02-12 08:19:30 +01:00
|
|
|
}
|
2017-10-02 22:48:13 +02:00
|
|
|
for string_id, error_msg in errors.items():
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=string_id, realm_name=realm_name
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-10-02 22:48:13 +02:00
|
|
|
self.assert_in_response(error_msg, result)
|
|
|
|
|
|
|
|
# test valid subdomain
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain="a-0", realm_name=realm_name
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
|
|
|
self.client_get(result["Location"])
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
self.client_get(confirmation_url)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
2021-02-12 08:20:45 +01:00
|
|
|
email, password, realm_subdomain="a-0", realm_name=realm_name
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-10-02 22:48:13 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].startswith("http://a-0.testserver/accounts/login/subdomain/")
|
|
|
|
)
|
2017-10-19 08:30:40 +02:00
|
|
|
|
2020-12-18 20:17:20 +01:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
|
|
|
def test_create_realm_using_old_subdomain_of_a_realm(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
2020-12-06 20:04:33 +01:00
|
|
|
do_change_realm_subdomain(realm, "new-name", acting_user=None)
|
2020-12-18 20:17:20 +01:00
|
|
|
|
|
|
|
email = "user1@test.com"
|
|
|
|
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(email, realm_subdomain="test", realm_name="Test")
|
2020-12-18 20:17:20 +01:00
|
|
|
self.assert_in_response("Subdomain unavailable. Please choose a different one.", result)
|
|
|
|
|
2017-10-19 08:30:40 +02:00
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_subdomain_restrictions_root_domain(self) -> None:
|
2017-10-19 08:30:40 +02:00
|
|
|
password = "test"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
# test root domain will fail with ROOT_DOMAIN_LANDING_PAGE
|
|
|
|
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True):
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain="", realm_name=realm_name
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_response("unavailable", result)
|
2017-10-19 08:30:40 +02:00
|
|
|
|
|
|
|
# test valid use of root domain
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(email, realm_subdomain="", realm_name=realm_name)
|
2023-03-03 11:58:00 +01:00
|
|
|
self.client_get(result["Location"])
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
self.client_get(confirmation_url)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
2021-02-12 08:20:45 +01:00
|
|
|
email, password, realm_subdomain="", realm_name=realm_name
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-10-19 08:30:40 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].startswith("http://testserver/accounts/login/subdomain/")
|
|
|
|
)
|
2017-10-19 08:30:40 +02:00
|
|
|
|
|
|
|
@override_settings(OPEN_REALM_CREATION=True)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_subdomain_restrictions_root_domain_option(self) -> None:
|
2017-10-19 08:30:40 +02:00
|
|
|
password = "test"
|
|
|
|
email = "user1@test.com"
|
|
|
|
realm_name = "Test"
|
|
|
|
|
|
|
|
# test root domain will fail with ROOT_DOMAIN_LANDING_PAGE
|
|
|
|
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True):
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain="abcdef", realm_name=realm_name, realm_in_root_domain="true"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_response("unavailable", result)
|
2017-10-19 08:30:40 +02:00
|
|
|
|
|
|
|
# test valid use of root domain
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain="abcdef", realm_name=realm_name, realm_in_root_domain="true"
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2023-03-21 14:11:44 +01:00
|
|
|
|
2023-03-03 11:58:00 +01:00
|
|
|
self.client_get(result["Location"])
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
self.client_get(confirmation_url)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
2021-02-12 08:20:45 +01:00
|
|
|
realm_subdomain="abcdef",
|
|
|
|
realm_in_root_domain="true",
|
2021-02-12 08:19:30 +01:00
|
|
|
realm_name=realm_name,
|
|
|
|
)
|
2017-10-19 08:30:40 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].startswith("http://testserver/accounts/login/subdomain/")
|
|
|
|
)
|
2016-10-31 23:28:20 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_is_root_domain_available(self) -> None:
|
2017-10-19 07:42:03 +02:00
|
|
|
self.assertTrue(is_root_domain_available())
|
|
|
|
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True):
|
|
|
|
self.assertFalse(is_root_domain_available())
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.string_id = Realm.SUBDOMAIN_FOR_ROOT_DOMAIN
|
|
|
|
realm.save()
|
|
|
|
self.assertFalse(is_root_domain_available())
|
|
|
|
|
2018-01-25 19:08:40 +01:00
|
|
|
def test_subdomain_check_api(self) -> None:
|
|
|
|
result = self.client_get("/json/realm/subdomain/zulip")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
["Subdomain unavailable. Please choose a different one."], result
|
|
|
|
)
|
2018-01-25 19:08:40 +01:00
|
|
|
|
|
|
|
result = self.client_get("/json/realm/subdomain/zu_lip")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
2021-02-12 08:20:45 +01:00
|
|
|
["Subdomain can only have lowercase letters, numbers, and '-'s."], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2018-01-25 19:08:40 +01:00
|
|
|
|
2021-08-23 15:14:05 +02:00
|
|
|
with self.settings(SOCIAL_AUTH_SUBDOMAIN="zulipauth"):
|
|
|
|
result = self.client_get("/json/realm/subdomain/zulipauth")
|
|
|
|
self.assert_in_success_response(
|
|
|
|
["Subdomain unavailable. Please choose a different one."], result
|
|
|
|
)
|
|
|
|
|
2018-01-25 19:08:40 +01:00
|
|
|
result = self.client_get("/json/realm/subdomain/hufflepuff")
|
|
|
|
self.assert_in_success_response(["available"], result)
|
|
|
|
self.assert_not_in_success_response(["unavailable"], result)
|
|
|
|
|
2018-04-23 20:02:45 +02:00
|
|
|
def test_subdomain_check_management_command(self) -> None:
|
2020-12-19 18:44:53 +01:00
|
|
|
# Short names should not work, even with the flag
|
2018-04-23 20:02:45 +02:00
|
|
|
with self.assertRaises(ValidationError):
|
2021-02-12 08:20:45 +01:00
|
|
|
check_subdomain_available("aa")
|
2020-12-19 18:44:53 +01:00
|
|
|
with self.assertRaises(ValidationError):
|
2021-02-12 08:20:45 +01:00
|
|
|
check_subdomain_available("aa", allow_reserved_subdomain=True)
|
2020-12-19 18:44:53 +01:00
|
|
|
|
|
|
|
# Malformed names should never work
|
|
|
|
with self.assertRaises(ValidationError):
|
2021-02-12 08:20:45 +01:00
|
|
|
check_subdomain_available("-ba_d-")
|
2020-12-19 18:44:53 +01:00
|
|
|
with self.assertRaises(ValidationError):
|
2021-02-12 08:20:45 +01:00
|
|
|
check_subdomain_available("-ba_d-", allow_reserved_subdomain=True)
|
2020-12-19 18:44:53 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.lib.name_restrictions.is_reserved_subdomain", return_value=False):
|
2020-12-19 18:44:53 +01:00
|
|
|
# Existing realms should never work even if they are not reserved keywords
|
|
|
|
with self.assertRaises(ValidationError):
|
2021-02-12 08:20:45 +01:00
|
|
|
check_subdomain_available("zulip")
|
2020-12-19 18:44:53 +01:00
|
|
|
with self.assertRaises(ValidationError):
|
2021-02-12 08:20:45 +01:00
|
|
|
check_subdomain_available("zulip", allow_reserved_subdomain=True)
|
2020-12-19 18:44:53 +01:00
|
|
|
|
|
|
|
# Reserved ones should only work with the flag
|
|
|
|
with self.assertRaises(ValidationError):
|
2021-02-12 08:20:45 +01:00
|
|
|
check_subdomain_available("stream")
|
|
|
|
check_subdomain_available("stream", allow_reserved_subdomain=True)
|
2018-04-23 20:02:45 +02:00
|
|
|
|
2016-11-04 01:23:43 +01:00
|
|
|
|
2023-03-09 02:27:46 +01:00
|
|
|
class UserSignUpTest(ZulipTestCase):
|
2022-06-08 04:52:09 +02:00
|
|
|
def _assert_redirected_to(self, result: "TestHttpResponse", url: str) -> None:
|
2017-08-17 18:28:21 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result["LOCATION"], url)
|
2017-08-17 18:28:21 +02:00
|
|
|
|
2021-06-23 11:31:36 +02:00
|
|
|
def verify_signup(
|
|
|
|
self,
|
|
|
|
*,
|
|
|
|
email: str = "newguy@zulip.com",
|
2021-06-24 23:54:50 +02:00
|
|
|
password: Optional[str] = "newpassword",
|
2021-06-23 11:31:36 +02:00
|
|
|
full_name: str = "New user's name",
|
|
|
|
realm: Optional[Realm] = None,
|
|
|
|
subdomain: Optional[str] = None,
|
2022-06-08 04:52:09 +02:00
|
|
|
) -> Union[UserProfile, "TestHttpResponse"]:
|
2021-06-23 11:31:36 +02:00
|
|
|
"""Common test function for signup tests. It is a goal to use this
|
|
|
|
common function for all signup tests to avoid code duplication; doing
|
|
|
|
so will likely require adding new parameters."""
|
|
|
|
|
2021-06-25 00:24:11 +02:00
|
|
|
if realm is None: # nocoverage
|
2021-06-23 11:31:36 +02:00
|
|
|
realm = get_realm("zulip")
|
|
|
|
|
|
|
|
client_kwargs: Dict[str, Any] = {}
|
|
|
|
if subdomain:
|
|
|
|
client_kwargs["subdomain"] = subdomain
|
|
|
|
|
|
|
|
result = self.client_post("/accounts/home/", {"email": email}, **client_kwargs)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2021-06-23 11:31:36 +02:00
|
|
|
result = self.client_get(result["Location"], **client_kwargs)
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2021-06-23 11:31:36 +02:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url, **client_kwargs)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
# Pick a password and agree to the ToS. This should create our
|
|
|
|
# account, log us in, and redirect to the app.
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email, password, full_name=full_name, **client_kwargs
|
|
|
|
)
|
|
|
|
|
2021-06-24 09:23:39 +02:00
|
|
|
if result.status_code == 200:
|
|
|
|
# This usually indicated an error returned when submitting the form.
|
|
|
|
# Return the result for the caller to deal with reacting to this, since
|
|
|
|
# in many tests this is expected and the caller wants to assert the content
|
|
|
|
# of the error.
|
|
|
|
return result
|
|
|
|
|
2021-06-23 11:31:36 +02:00
|
|
|
# Verify that we were served a redirect to the app.
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-09-13 15:48:33 +02:00
|
|
|
self.assertEqual(result["Location"], f"{realm.uri}/")
|
2021-06-23 11:31:36 +02:00
|
|
|
|
|
|
|
# Verify that we successfully logged in.
|
2021-09-13 15:52:29 +02:00
|
|
|
user_profile = get_user_by_delivery_email(email, realm)
|
2021-06-23 11:31:36 +02:00
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
|
|
|
return user_profile
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_bad_email_configuration_for_accounts_home(self) -> None:
|
2017-08-17 18:28:21 +02:00
|
|
|
"""
|
2022-11-17 09:30:48 +01:00
|
|
|
Make sure we redirect for EmailNotDeliveredError.
|
2017-08-17 18:28:21 +02:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.nonreg_email("newguy")
|
2017-08-17 18:28:21 +02:00
|
|
|
|
|
|
|
smtp_mock = patch(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zerver.views.registration.send_confirm_registration_email",
|
2022-11-17 09:30:48 +01:00
|
|
|
side_effect=EmailNotDeliveredError,
|
2017-08-17 18:28:21 +02:00
|
|
|
)
|
|
|
|
|
2020-12-23 21:45:16 +01:00
|
|
|
with smtp_mock, self.assertLogs(level="ERROR") as m:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2017-08-17 18:28:21 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self._assert_redirected_to(result, "/config-error/smtp")
|
2021-04-03 12:14:01 +02:00
|
|
|
self.assertEqual(m.output, ["ERROR:root:Error in accounts_home"])
|
2017-08-17 18:28:21 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_bad_email_configuration_for_create_realm(self) -> None:
|
2017-08-17 19:59:17 +02:00
|
|
|
"""
|
2022-11-17 09:30:48 +01:00
|
|
|
Make sure we redirect for EmailNotDeliveredError.
|
2017-08-17 19:59:17 +02:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.nonreg_email("newguy")
|
2017-08-17 19:59:17 +02:00
|
|
|
|
|
|
|
smtp_mock = patch(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zerver.views.registration.send_confirm_registration_email",
|
2022-11-17 09:30:48 +01:00
|
|
|
side_effect=EmailNotDeliveredError,
|
2017-08-17 19:59:17 +02:00
|
|
|
)
|
|
|
|
|
2020-12-23 21:45:16 +01:00
|
|
|
with smtp_mock, self.assertLogs(level="ERROR") as m:
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain="zuliptest", realm_name="Zulip test"
|
2023-03-03 11:58:00 +01:00
|
|
|
)
|
2017-08-17 19:59:17 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self._assert_redirected_to(result, "/config-error/smtp")
|
2021-04-03 12:14:01 +02:00
|
|
|
self.assertEqual(m.output, ["ERROR:root:Error in create_realm"])
|
2017-08-17 19:59:17 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_user_default_language_and_timezone(self) -> None:
|
2016-11-04 01:23:43 +01:00
|
|
|
"""
|
2021-04-04 16:58:41 +02:00
|
|
|
Check if the default language of new user is set using the browser locale
|
2016-11-04 01:23:43 +01:00
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.nonreg_email("newguy")
|
2016-11-04 01:23:43 +01:00
|
|
|
password = "newpassword"
|
2022-02-10 01:45:44 +01:00
|
|
|
timezone = "America/Denver"
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2021-03-01 11:33:24 +01:00
|
|
|
do_set_realm_property(realm, "default_language", "de", acting_user=None)
|
2016-11-04 01:23:43 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2016-11-04 01:23:43 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2016-11-04 01:23:43 +01:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2016-11-04 01:23:43 +01:00
|
|
|
|
|
|
|
# Pick a password and agree to the ToS.
|
2021-04-04 16:58:41 +02:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email, password, timezone=timezone, HTTP_ACCEPT_LANGUAGE="fr,en;q=0.9"
|
|
|
|
)
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2016-11-04 01:23:43 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.nonreg_user("newguy")
|
2021-04-04 16:58:41 +02:00
|
|
|
self.assertNotEqual(user_profile.default_language, realm.default_language)
|
|
|
|
self.assertEqual(user_profile.default_language, "fr")
|
2017-05-04 15:20:25 +02:00
|
|
|
self.assertEqual(user_profile.timezone, timezone)
|
2016-11-04 01:23:43 +01:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-11-04 01:23:43 +01:00
|
|
|
outbox.pop()
|
2021-04-04 16:58:41 +02:00
|
|
|
|
|
|
|
def test_default_language_with_unsupported_browser_locale(self) -> None:
|
|
|
|
email = self.nonreg_email("newguy")
|
|
|
|
password = "newpassword"
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
do_set_realm_property(realm, "default_language", "de", acting_user=None)
|
|
|
|
|
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2021-04-04 16:58:41 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2021-04-04 16:58:41 +02:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
# Pick a password and agree to the ToS.
|
|
|
|
result = self.submit_reg_form_for_user(email, password, HTTP_ACCEPT_LANGUAGE="en-IND")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
user_profile = self.nonreg_user("newguy")
|
|
|
|
self.assertEqual(user_profile.default_language, realm.default_language)
|
|
|
|
from django.core.mail import outbox
|
|
|
|
|
|
|
|
outbox.pop()
|
2016-11-04 01:23:43 +01:00
|
|
|
|
2018-03-30 22:38:16 +02:00
|
|
|
def test_default_twenty_four_hour_time(self) -> None:
|
|
|
|
"""
|
|
|
|
Check if the default twenty_four_hour_time setting of new user
|
|
|
|
is the default twenty_four_hour_time of the realm.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.nonreg_email("newguy")
|
2018-03-30 22:38:16 +02:00
|
|
|
password = "newpassword"
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2021-09-17 18:11:37 +02:00
|
|
|
realm_user_default = RealmUserDefault.objects.get(realm=realm)
|
|
|
|
do_set_realm_user_default_setting(
|
|
|
|
realm_user_default, "twenty_four_hour_time", True, acting_user=None
|
|
|
|
)
|
2018-03-30 22:38:16 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2018-03-30 22:38:16 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2018-03-30 22:38:16 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2018-03-30 22:38:16 +02:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
result = self.submit_reg_form_for_user(email, password)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.nonreg_user("newguy")
|
2021-09-17 18:11:37 +02:00
|
|
|
realm_user_default = RealmUserDefault.objects.get(realm=realm)
|
|
|
|
self.assertEqual(
|
|
|
|
user_profile.twenty_four_hour_time, realm_user_default.twenty_four_hour_time
|
|
|
|
)
|
2018-03-30 22:38:16 +02:00
|
|
|
|
2023-02-13 17:40:16 +01:00
|
|
|
def test_email_address_visibility_for_new_user(self) -> None:
|
|
|
|
email = self.nonreg_email("newguy")
|
|
|
|
password = "newpassword"
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm_user_default = RealmUserDefault.objects.get(realm=realm)
|
|
|
|
self.assertEqual(
|
|
|
|
realm_user_default.email_address_visibility, UserProfile.EMAIL_ADDRESS_VISIBILITY_ADMINS
|
|
|
|
)
|
|
|
|
|
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
self.assert_in_response("Check your email", result)
|
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
# Pick a password and agree to the ToS.
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email, password, email_address_visibility=UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY
|
|
|
|
)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
# Realm-level default is overridden by the value passed during signup.
|
|
|
|
user_profile = self.nonreg_user("newguy")
|
|
|
|
self.assertEqual(
|
|
|
|
user_profile.email_address_visibility, UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY
|
|
|
|
)
|
|
|
|
from django.core.mail import outbox
|
|
|
|
|
|
|
|
outbox.pop()
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_signup_already_active(self) -> None:
|
2017-03-11 20:12:01 +01:00
|
|
|
"""
|
|
|
|
Check if signing up with an active email redirects to a login page.
|
|
|
|
"""
|
2017-05-25 01:40:26 +02:00
|
|
|
email = self.example_email("hamlet")
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2017-03-11 20:12:01 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIn("login", result["Location"])
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2017-08-28 08:13:15 +02:00
|
|
|
self.assert_in_response("You've already registered", result)
|
2017-03-11 20:12:01 +01:00
|
|
|
|
2017-11-28 02:49:50 +01:00
|
|
|
def test_signup_system_bot(self) -> None:
|
|
|
|
email = "notification-bot@zulip.com"
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email}, subdomain="lear")
|
2017-11-28 02:49:50 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertIn("login", result["Location"])
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2017-11-28 02:49:50 +01:00
|
|
|
|
|
|
|
# This is not really the right error message, but at least it's an error.
|
|
|
|
self.assert_in_response("You've already registered", result)
|
|
|
|
|
2017-11-25 23:26:15 +01:00
|
|
|
def test_signup_existing_email(self) -> None:
|
|
|
|
"""
|
|
|
|
Check if signing up with an email used in another realm succeeds.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.example_email("hamlet")
|
2021-06-23 11:31:36 +02:00
|
|
|
self.verify_signup(email=email, realm=get_realm("lear"), subdomain="lear")
|
2020-03-12 14:17:25 +01:00
|
|
|
self.assertEqual(UserProfile.objects.filter(delivery_email=email).count(), 2)
|
2017-11-25 23:26:15 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_signup_invalid_name(self) -> None:
|
2017-02-08 05:04:14 +01:00
|
|
|
"""
|
2017-03-11 20:10:15 +01:00
|
|
|
Check if an invalid name during signup is handled properly.
|
2017-02-08 05:04:14 +01:00
|
|
|
"""
|
|
|
|
|
2021-06-24 09:23:39 +02:00
|
|
|
result = self.verify_signup(full_name="<invalid>")
|
2022-06-08 04:52:09 +02:00
|
|
|
# _WSGIPatchedWSGIResponse does not exist in Django, thus the inverted isinstance check.
|
|
|
|
assert not isinstance(result, UserProfile)
|
2017-03-18 22:48:44 +01:00
|
|
|
self.assert_in_success_response(["Invalid characters in name!"], result)
|
2017-02-08 05:04:14 +01:00
|
|
|
|
2017-08-09 22:09:38 +02:00
|
|
|
# Verify that the user is asked for name and password
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_success_response(["id_password", "id_full_name"], result)
|
2017-08-09 22:09:38 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_signup_without_password(self) -> None:
|
2017-03-19 01:48:12 +01:00
|
|
|
"""
|
|
|
|
Check if signing up without a password works properly when
|
|
|
|
password_auth_enabled is False.
|
|
|
|
"""
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.nonreg_email("newuser")
|
|
|
|
with patch("zerver.views.registration.password_auth_enabled", return_value=False):
|
2021-06-24 10:14:28 +02:00
|
|
|
user_profile = self.verify_signup(email=email, password=None)
|
2017-03-19 01:48:12 +01:00
|
|
|
|
2021-06-24 10:14:28 +02:00
|
|
|
assert isinstance(user_profile, UserProfile)
|
2017-03-19 01:48:12 +01:00
|
|
|
# User should now be logged in.
|
2019-05-26 22:12:46 +02:00
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
2017-03-19 01:48:12 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_signup_without_full_name(self) -> None:
|
2017-03-18 20:36:40 +01:00
|
|
|
"""
|
|
|
|
Check if signing up without a full name redirects to a registration
|
|
|
|
form.
|
|
|
|
"""
|
|
|
|
email = "newguy@zulip.com"
|
|
|
|
password = "newpassword"
|
2021-06-24 20:00:23 +02:00
|
|
|
result = self.verify_signup(email=email, password=password, full_name="")
|
2022-06-08 04:52:09 +02:00
|
|
|
# _WSGIPatchedWSGIResponse does not exist in Django, thus the inverted isinstance check.
|
|
|
|
assert not isinstance(result, UserProfile)
|
2023-03-09 12:30:03 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
["Enter your account details to complete registration."], result
|
|
|
|
)
|
2017-03-18 20:36:40 +01:00
|
|
|
|
2017-08-09 22:09:38 +02:00
|
|
|
# Verify that the user is asked for name and password
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_success_response(["id_password", "id_full_name"], result)
|
2017-08-09 22:09:38 +02:00
|
|
|
|
2020-06-14 13:32:38 +02:00
|
|
|
def test_signup_email_message_contains_org_header(self) -> None:
|
|
|
|
email = "newguy@zulip.com"
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2020-06-14 13:32:38 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2020-06-14 13:32:38 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2020-06-14 13:32:38 +02:00
|
|
|
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-06-14 13:32:38 +02:00
|
|
|
self.assertEqual(outbox[0].extra_headers["List-Id"], "Zulip Dev <zulip.testserver>")
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_signup_with_full_name(self) -> None:
|
2017-04-20 08:25:15 +02:00
|
|
|
"""
|
|
|
|
Check if signing up without a full name redirects to a registration
|
|
|
|
form.
|
|
|
|
"""
|
|
|
|
email = "newguy@zulip.com"
|
|
|
|
password = "newpassword"
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2017-04-20 08:25:15 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-04-20 08:25:15 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2017-04-20 08:25:15 +02:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/register/",
|
2021-02-12 08:19:30 +01:00
|
|
|
{
|
2021-02-12 08:20:45 +01:00
|
|
|
"password": password,
|
|
|
|
"key": find_key_by_email(email),
|
|
|
|
"terms": True,
|
|
|
|
"full_name": "New Guy",
|
|
|
|
"from_confirmation": "1",
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
2023-03-09 12:30:03 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
["Enter your account details to complete registration."], result
|
|
|
|
)
|
2017-04-20 08:25:15 +02:00
|
|
|
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
def test_signup_with_weak_password(self) -> None:
|
|
|
|
"""
|
|
|
|
Check if signing up without a full name redirects to a registration
|
|
|
|
form.
|
|
|
|
"""
|
|
|
|
email = "newguy@zulip.com"
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
with self.settings(PASSWORD_MIN_LENGTH=6, PASSWORD_MIN_GUESSES=1000):
|
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/register/",
|
2021-02-12 08:19:30 +01:00
|
|
|
{
|
2021-02-12 08:20:45 +01:00
|
|
|
"password": "easy",
|
|
|
|
"key": find_key_by_email(email),
|
|
|
|
"terms": True,
|
|
|
|
"full_name": "New Guy",
|
|
|
|
"from_confirmation": "1",
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
2023-03-09 12:30:03 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
["Enter your account details to complete registration."], result
|
|
|
|
)
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.submit_reg_form_for_user(email, "easy", full_name="New Guy")
|
auth: Use zxcvbn to ensure password strength on server side.
For a long time, we've been only doing the zxcvbn password strength
checks on the browser, which is helpful, but means users could through
hackery (or a bug in the frontend validation code) manage to set a
too-weak password. We fix this by running our password strength
validation on the backend as well, using python-zxcvbn.
In theory, a bug in python-zxcvbn could result in it producing a
different opinion than the frontend version; if so, it'd be a pretty
bad bug in the library, and hopefully we'd hear about it from users,
report upstream, and get it fixed that way. Alternatively, we can
switch to shelling out to node like we do for KaTeX.
Fixes #6880.
2019-11-18 08:11:03 +01:00
|
|
|
self.assert_in_success_response(["The password is too weak."], result)
|
|
|
|
with self.assertRaises(UserProfile.DoesNotExist):
|
|
|
|
# Account wasn't created.
|
|
|
|
get_user(email, get_realm("zulip"))
|
|
|
|
|
2017-11-16 23:00:04 +01:00
|
|
|
def test_signup_with_default_stream_group(self) -> None:
|
|
|
|
# Check if user is subscribed to the streams of default
|
|
|
|
# stream group as well as default streams.
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.nonreg_email("newguy")
|
2017-11-16 23:00:04 +01:00
|
|
|
password = "newpassword"
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2017-11-16 23:00:04 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
default_streams = []
|
2021-04-29 17:22:48 +02:00
|
|
|
|
|
|
|
existing_default_streams = DefaultStream.objects.filter(realm=realm)
|
|
|
|
self.assert_length(existing_default_streams, 1)
|
|
|
|
self.assertEqual(existing_default_streams[0].stream.name, "Verona")
|
|
|
|
default_streams.append(existing_default_streams[0].stream)
|
|
|
|
|
|
|
|
for stream_name in ["venice", "rome"]:
|
2017-11-16 23:00:04 +01:00
|
|
|
stream = get_stream(stream_name, realm)
|
|
|
|
do_add_default_stream(stream)
|
|
|
|
default_streams.append(stream)
|
|
|
|
|
|
|
|
group1_streams = []
|
|
|
|
for stream_name in ["scotland", "denmark"]:
|
|
|
|
stream = get_stream(stream_name, realm)
|
|
|
|
group1_streams.append(stream)
|
|
|
|
do_create_default_stream_group(realm, "group 1", "group 1 description", group1_streams)
|
|
|
|
|
|
|
|
result = self.submit_reg_form_for_user(email, password, default_stream_groups=["group 1"])
|
|
|
|
self.check_user_subscribed_only_to_streams("newguy", default_streams + group1_streams)
|
|
|
|
|
2019-11-03 23:02:37 +01:00
|
|
|
def test_signup_two_confirmation_links(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.nonreg_email("newguy")
|
2019-11-03 23:02:37 +01:00
|
|
|
password = "newpassword"
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2019-11-03 23:02:37 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
first_confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
first_confirmation_key = find_key_by_email(email)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2019-11-03 23:02:37 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
second_confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
|
|
|
|
# Sanity check:
|
|
|
|
self.assertNotEqual(first_confirmation_url, second_confirmation_url)
|
|
|
|
|
|
|
|
# Register the account (this will use the second confirmation url):
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email, password, full_name="New Guy", from_confirmation="1"
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
2023-03-09 12:30:03 +01:00
|
|
|
["Enter your account details to complete registration.", "New Guy", email], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
result = self.submit_reg_form_for_user(email, password, full_name="New Guy")
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
|
|
|
self.assertEqual(user_profile.delivery_email, email)
|
2019-11-03 23:02:37 +01:00
|
|
|
|
|
|
|
# Now try to to register using the first confirmation url:
|
|
|
|
result = self.client_get(first_confirmation_url)
|
2021-11-30 12:51:22 +01:00
|
|
|
self.assertEqual(result.status_code, 404)
|
2019-11-03 23:02:37 +01:00
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/register/",
|
2021-02-12 08:19:30 +01:00
|
|
|
{
|
2021-02-12 08:20:45 +01:00
|
|
|
"password": password,
|
|
|
|
"key": first_confirmation_key,
|
|
|
|
"terms": True,
|
|
|
|
"full_name": "New Guy",
|
|
|
|
"from_confirmation": "1",
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
2020-05-01 01:52:45 +02:00
|
|
|
# Error page should be displayed
|
2021-05-21 00:10:00 +02:00
|
|
|
self.assertEqual(result.status_code, 404)
|
2021-12-02 17:55:25 +01:00
|
|
|
self.assert_in_response(
|
|
|
|
"Whoops. The confirmation link has expired or been deactivated.", result
|
|
|
|
)
|
2019-11-03 23:02:37 +01:00
|
|
|
|
2017-11-16 23:00:04 +01:00
|
|
|
def test_signup_with_multiple_default_stream_groups(self) -> None:
|
|
|
|
# Check if user is subscribed to the streams of default
|
|
|
|
# stream groups as well as default streams.
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.nonreg_email("newguy")
|
2017-11-16 23:00:04 +01:00
|
|
|
password = "newpassword"
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2017-11-16 23:00:04 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
result = self.client_get(result["Location"])
|
|
|
|
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2021-04-29 17:22:48 +02:00
|
|
|
DefaultStream.objects.filter(realm=realm).delete()
|
2017-11-16 23:00:04 +01:00
|
|
|
default_streams = []
|
|
|
|
for stream_name in ["venice", "verona"]:
|
|
|
|
stream = get_stream(stream_name, realm)
|
|
|
|
do_add_default_stream(stream)
|
|
|
|
default_streams.append(stream)
|
|
|
|
|
|
|
|
group1_streams = []
|
|
|
|
for stream_name in ["scotland", "denmark"]:
|
|
|
|
stream = get_stream(stream_name, realm)
|
|
|
|
group1_streams.append(stream)
|
|
|
|
do_create_default_stream_group(realm, "group 1", "group 1 description", group1_streams)
|
|
|
|
|
|
|
|
group2_streams = []
|
|
|
|
for stream_name in ["scotland", "rome"]:
|
|
|
|
stream = get_stream(stream_name, realm)
|
|
|
|
group2_streams.append(stream)
|
|
|
|
do_create_default_stream_group(realm, "group 2", "group 2 description", group2_streams)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email, password, default_stream_groups=["group 1", "group 2"]
|
|
|
|
)
|
2017-11-17 07:00:53 +01:00
|
|
|
self.check_user_subscribed_only_to_streams(
|
2021-02-12 08:19:30 +01:00
|
|
|
"newguy", list(set(default_streams + group1_streams + group2_streams))
|
|
|
|
)
|
2017-11-16 23:00:04 +01:00
|
|
|
|
2018-05-22 18:13:51 +02:00
|
|
|
def test_signup_without_user_settings_from_another_realm(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet_in_zulip = self.example_user("hamlet")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = hamlet_in_zulip.delivery_email
|
2018-05-22 18:13:51 +02:00
|
|
|
password = "newpassword"
|
|
|
|
subdomain = "lear"
|
|
|
|
realm = get_realm("lear")
|
|
|
|
|
|
|
|
# Make an account in the Zulip realm, but we're not copying from there.
|
|
|
|
hamlet_in_zulip.left_side_userlist = True
|
|
|
|
hamlet_in_zulip.default_language = "de"
|
|
|
|
hamlet_in_zulip.emojiset = "twitter"
|
|
|
|
hamlet_in_zulip.high_contrast_mode = True
|
2018-06-13 20:16:51 +02:00
|
|
|
hamlet_in_zulip.enter_sends = True
|
2018-06-13 14:10:53 +02:00
|
|
|
hamlet_in_zulip.tutorial_status = UserProfile.TUTORIAL_FINISHED
|
2021-10-26 09:15:16 +02:00
|
|
|
hamlet_in_zulip.email_address_visibility = UserProfile.EMAIL_ADDRESS_VISIBILITY_EVERYONE
|
2018-05-22 18:13:51 +02:00
|
|
|
hamlet_in_zulip.save()
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email}, subdomain=subdomain)
|
2018-05-22 18:13:51 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
result = self.client_get(result["Location"], subdomain=subdomain)
|
|
|
|
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url, subdomain=subdomain)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
2020-12-04 19:45:58 +01:00
|
|
|
email, password, source_realm_id="", HTTP_HOST=subdomain + ".testserver"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2018-05-22 18:13:51 +02:00
|
|
|
|
|
|
|
hamlet = get_user(self.example_email("hamlet"), realm)
|
|
|
|
self.assertEqual(hamlet.left_side_userlist, False)
|
|
|
|
self.assertEqual(hamlet.default_language, "en")
|
2021-08-27 21:30:55 +02:00
|
|
|
self.assertEqual(hamlet.emojiset, "google")
|
2018-05-22 18:13:51 +02:00
|
|
|
self.assertEqual(hamlet.high_contrast_mode, False)
|
2019-06-11 08:47:49 +02:00
|
|
|
self.assertEqual(hamlet.enable_stream_audible_notifications, False)
|
2018-06-13 20:16:51 +02:00
|
|
|
self.assertEqual(hamlet.enter_sends, False)
|
2018-06-13 14:10:53 +02:00
|
|
|
self.assertEqual(hamlet.tutorial_status, UserProfile.TUTORIAL_WAITING)
|
2018-05-22 18:13:51 +02:00
|
|
|
|
|
|
|
def test_signup_with_user_settings_from_another_realm(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
hamlet_in_zulip = self.example_user("hamlet")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = hamlet_in_zulip.delivery_email
|
2018-05-22 18:13:51 +02:00
|
|
|
password = "newpassword"
|
|
|
|
subdomain = "lear"
|
|
|
|
lear_realm = get_realm("lear")
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("hamlet")
|
|
|
|
with get_test_image_file("img.png") as image_file:
|
|
|
|
self.client_post("/json/users/me/avatar", {"file": image_file})
|
2020-03-12 14:17:25 +01:00
|
|
|
hamlet_in_zulip.refresh_from_db()
|
2018-05-22 18:13:51 +02:00
|
|
|
hamlet_in_zulip.left_side_userlist = True
|
|
|
|
hamlet_in_zulip.default_language = "de"
|
|
|
|
hamlet_in_zulip.emojiset = "twitter"
|
|
|
|
hamlet_in_zulip.high_contrast_mode = True
|
2018-06-13 20:16:51 +02:00
|
|
|
hamlet_in_zulip.enter_sends = True
|
2018-06-13 14:10:53 +02:00
|
|
|
hamlet_in_zulip.tutorial_status = UserProfile.TUTORIAL_FINISHED
|
2021-10-26 09:15:16 +02:00
|
|
|
hamlet_in_zulip.email_address_visibility = UserProfile.EMAIL_ADDRESS_VISIBILITY_EVERYONE
|
2018-05-22 18:13:51 +02:00
|
|
|
hamlet_in_zulip.save()
|
|
|
|
|
2022-01-23 20:30:46 +01:00
|
|
|
# Now we'll be making requests to another subdomain, so we need to logout
|
|
|
|
# to avoid polluting the session in the test environment by still being
|
|
|
|
# logged in.
|
|
|
|
self.logout()
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email}, subdomain=subdomain)
|
2018-05-22 18:13:51 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
result = self.client_get(result["Location"], subdomain=subdomain)
|
|
|
|
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url, subdomain=subdomain)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
2018-12-25 13:45:01 +01:00
|
|
|
|
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/register/",
|
|
|
|
{"password": password, "key": find_key_by_email(email), "from_confirmation": "1"},
|
2021-02-12 08:19:30 +01:00
|
|
|
subdomain=subdomain,
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
"Import settings from existing Zulip account",
|
2022-12-27 10:39:26 +01:00
|
|
|
"selected >\n Zulip Dev",
|
2023-03-09 12:30:03 +01:00
|
|
|
"Enter your account details to complete registration.",
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2018-12-25 13:45:01 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
2020-11-16 19:33:10 +01:00
|
|
|
email,
|
|
|
|
password,
|
2020-12-04 19:45:58 +01:00
|
|
|
source_realm_id=str(hamlet_in_zulip.realm.id),
|
2020-11-16 19:33:10 +01:00
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
2023-02-13 17:40:16 +01:00
|
|
|
email_address_visibility=UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2018-05-22 18:13:51 +02:00
|
|
|
|
2023-02-13 17:40:16 +01:00
|
|
|
hamlet_in_lear = get_user_by_delivery_email(email, lear_realm)
|
2018-05-22 18:13:51 +02:00
|
|
|
self.assertEqual(hamlet_in_lear.left_side_userlist, True)
|
|
|
|
self.assertEqual(hamlet_in_lear.default_language, "de")
|
|
|
|
self.assertEqual(hamlet_in_lear.emojiset, "twitter")
|
|
|
|
self.assertEqual(hamlet_in_lear.high_contrast_mode, True)
|
2018-06-13 20:16:51 +02:00
|
|
|
self.assertEqual(hamlet_in_lear.enter_sends, True)
|
2019-06-11 08:47:49 +02:00
|
|
|
self.assertEqual(hamlet_in_lear.enable_stream_audible_notifications, False)
|
2018-06-13 14:10:53 +02:00
|
|
|
self.assertEqual(hamlet_in_lear.tutorial_status, UserProfile.TUTORIAL_FINISHED)
|
2021-10-26 09:15:16 +02:00
|
|
|
self.assertEqual(
|
2023-02-13 17:40:16 +01:00
|
|
|
hamlet_in_lear.email_address_visibility, UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY
|
2021-10-26 09:15:16 +02:00
|
|
|
)
|
2020-03-12 14:17:25 +01:00
|
|
|
|
2018-06-06 14:30:26 +02:00
|
|
|
zulip_path_id = avatar_disk_path(hamlet_in_zulip)
|
2020-03-12 14:17:25 +01:00
|
|
|
lear_path_id = avatar_disk_path(hamlet_in_lear)
|
2021-02-12 08:20:45 +01:00
|
|
|
with open(zulip_path_id, "rb") as f:
|
2020-10-24 09:33:54 +02:00
|
|
|
zulip_avatar_bits = f.read()
|
2021-02-12 08:20:45 +01:00
|
|
|
with open(lear_path_id, "rb") as f:
|
2020-10-24 09:33:54 +02:00
|
|
|
lear_avatar_bits = f.read()
|
2020-03-12 14:17:25 +01:00
|
|
|
|
2021-07-13 19:42:37 +02:00
|
|
|
self.assertGreater(len(zulip_avatar_bits), 500)
|
2020-03-12 14:17:25 +01:00
|
|
|
self.assertEqual(zulip_avatar_bits, lear_avatar_bits)
|
2018-05-22 18:13:51 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_signup_invalid_subdomain(self) -> None:
|
2017-03-19 01:48:12 +01:00
|
|
|
"""
|
|
|
|
Check if attempting to authenticate to the wrong subdomain logs an
|
|
|
|
error and redirects.
|
|
|
|
"""
|
|
|
|
email = "newuser@zulip.com"
|
|
|
|
password = "newpassword"
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/home/", {"email": email})
|
2017-03-19 01:48:12 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-03-19 01:48:12 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2017-03-19 01:48:12 +01:00
|
|
|
|
|
|
|
# Visit the confirmation link.
|
|
|
|
confirmation_url = self.get_confirmation_url_from_outbox(email)
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def invalid_subdomain(**kwargs: Any) -> Any:
|
2021-02-12 08:20:45 +01:00
|
|
|
return_data = kwargs.get("return_data", {})
|
|
|
|
return_data["invalid_subdomain"] = True
|
2017-03-19 01:48:12 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.authenticate", side_effect=invalid_subdomain):
|
2020-12-23 21:45:16 +01:00
|
|
|
with self.assertLogs(level="ERROR") as m:
|
2017-03-19 01:48:12 +01:00
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/register/",
|
2021-02-12 08:19:30 +01:00
|
|
|
{
|
2021-02-12 08:20:45 +01:00
|
|
|
"password": password,
|
|
|
|
"full_name": "New User",
|
|
|
|
"key": find_key_by_email(email),
|
|
|
|
"terms": True,
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
m.output,
|
|
|
|
["ERROR:root:Subdomain mismatch in registration zulip: newuser@zulip.com"],
|
|
|
|
)
|
2017-03-19 01:48:12 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
2020-11-16 19:33:10 +01:00
|
|
|
def test_signup_using_invalid_subdomain_preserves_state_of_form(self) -> None:
|
|
|
|
"""
|
|
|
|
Check that when we give invalid subdomain and submit the registration form
|
|
|
|
all the values in the form are preserved.
|
|
|
|
"""
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
|
|
|
|
email = self.example_email("iago")
|
|
|
|
realm_name = "Test"
|
|
|
|
|
2023-03-21 14:11:44 +01:00
|
|
|
result = self.submit_realm_creation_form(
|
|
|
|
email, realm_subdomain=realm.string_id, realm_name=realm_name
|
2020-11-16 19:33:10 +01:00
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
|
|
|
"Subdomain unavailable. Please choose a different one.",
|
2023-03-03 11:58:00 +01:00
|
|
|
'value="Test"',
|
2021-05-02 20:35:32 +02:00
|
|
|
'name="realm_name"',
|
2020-11-16 19:33:10 +01:00
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
|
|
|
|
2017-11-08 22:02:59 +01:00
|
|
|
def test_replace_subdomain_in_confirmation_link(self) -> None:
|
|
|
|
"""
|
|
|
|
Check that manually changing the subdomain in a registration
|
|
|
|
confirmation link doesn't allow you to register to a different realm.
|
|
|
|
"""
|
|
|
|
email = "newuser@zulip.com"
|
2021-02-12 08:20:45 +01:00
|
|
|
self.client_post("/accounts/home/", {"email": email})
|
2017-11-08 22:02:59 +01:00
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/register/",
|
2021-02-12 08:19:30 +01:00
|
|
|
{
|
2021-02-12 08:20:45 +01:00
|
|
|
"password": "password",
|
|
|
|
"key": find_key_by_email(email),
|
|
|
|
"terms": True,
|
|
|
|
"full_name": "New User",
|
|
|
|
"from_confirmation": "1",
|
2021-02-12 08:19:30 +01:00
|
|
|
},
|
|
|
|
subdomain="zephyr",
|
|
|
|
)
|
2021-11-30 12:51:22 +01:00
|
|
|
self.assertEqual(result.status_code, 404)
|
|
|
|
self.assert_in_response("We couldn't find your confirmation link", result)
|
2017-11-08 22:02:59 +01:00
|
|
|
|
2021-05-28 15:57:08 +02:00
|
|
|
def test_signup_to_realm_on_manual_license_plan(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
denmark_stream = get_stream("Denmark", realm)
|
|
|
|
realm.signup_notifications_stream = denmark_stream
|
|
|
|
realm.save(update_fields=["signup_notifications_stream"])
|
|
|
|
|
|
|
|
_, ledger = self.subscribe_realm_to_monthly_plan_on_manual_license_management(realm, 5, 5)
|
|
|
|
|
|
|
|
with self.settings(BILLING_ENABLED=True):
|
|
|
|
form = HomepageForm({"email": self.nonreg_email("test")}, realm=realm)
|
|
|
|
self.assertIn(
|
|
|
|
"New members cannot join this organization because all Zulip licenses",
|
|
|
|
form.errors["email"][0],
|
|
|
|
)
|
|
|
|
last_message = Message.objects.last()
|
2021-07-24 16:56:39 +02:00
|
|
|
assert last_message is not None
|
2021-05-28 15:57:08 +02:00
|
|
|
self.assertIn(
|
|
|
|
f"A new member ({self.nonreg_email('test')}) was unable to join your organization because all Zulip",
|
|
|
|
last_message.content,
|
|
|
|
)
|
|
|
|
self.assertEqual(last_message.recipient.type_id, denmark_stream.id)
|
|
|
|
|
|
|
|
ledger.licenses_at_next_renewal = 50
|
|
|
|
ledger.save(update_fields=["licenses_at_next_renewal"])
|
|
|
|
with self.settings(BILLING_ENABLED=True):
|
|
|
|
form = HomepageForm({"email": self.nonreg_email("test")}, realm=realm)
|
|
|
|
self.assertIn(
|
|
|
|
"New members cannot join this organization because all Zulip licenses",
|
|
|
|
form.errors["email"][0],
|
|
|
|
)
|
|
|
|
|
|
|
|
ledger.licenses = 50
|
|
|
|
ledger.save(update_fields=["licenses"])
|
|
|
|
with self.settings(BILLING_ENABLED=True):
|
|
|
|
form = HomepageForm({"email": self.nonreg_email("test")}, realm=realm)
|
|
|
|
self.assertEqual(form.errors, {})
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_failed_signup_due_to_restricted_domain(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2021-03-01 11:33:24 +01:00
|
|
|
do_set_realm_property(realm, "invite_required", False, acting_user=None)
|
|
|
|
do_set_realm_property(realm, "emails_restricted_to_domains", True, acting_user=None)
|
2017-10-02 22:43:43 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "user@acme.com"
|
|
|
|
form = HomepageForm({"email": email}, realm=realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
2021-02-12 08:20:45 +01:00
|
|
|
f"Your email address, {email}, is not in one of the domains", form.errors["email"][0]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2016-11-09 00:47:27 +01:00
|
|
|
|
2018-03-05 20:19:07 +01:00
|
|
|
def test_failed_signup_due_to_disposable_email(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2018-07-27 23:26:29 +02:00
|
|
|
realm.emails_restricted_to_domains = False
|
2018-03-05 20:19:07 +01:00
|
|
|
realm.disallow_disposable_email_addresses = True
|
|
|
|
realm.save()
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "abc@mailnator.com"
|
|
|
|
form = HomepageForm({"email": email}, realm=realm)
|
|
|
|
self.assertIn("Please use your real email address", form.errors["email"][0])
|
2018-03-05 20:19:07 +01:00
|
|
|
|
2018-06-20 13:08:07 +02:00
|
|
|
def test_failed_signup_due_to_email_containing_plus(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2018-07-27 23:26:29 +02:00
|
|
|
realm.emails_restricted_to_domains = True
|
2018-06-20 13:08:07 +02:00
|
|
|
realm.save()
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "iago+label@zulip.com"
|
|
|
|
form = HomepageForm({"email": email}, realm=realm)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
|
|
|
"Email addresses containing + are not allowed in this organization.",
|
2021-02-12 08:20:45 +01:00
|
|
|
form.errors["email"][0],
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2018-06-20 13:08:07 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_failed_signup_due_to_invite_required(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2016-11-09 00:47:27 +01:00
|
|
|
realm.invite_required = True
|
|
|
|
realm.save()
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "user@zulip.com"
|
|
|
|
form = HomepageForm({"email": email}, realm=realm)
|
|
|
|
self.assertIn(f"Please request an invite for {email} from", form.errors["email"][0])
|
2016-11-09 00:47:27 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_failed_signup_due_to_nonexistent_realm(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
email = "user@acme.com"
|
|
|
|
form = HomepageForm({"email": email}, realm=None)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertIn(
|
|
|
|
f"organization you are trying to join using {email} does not exist",
|
2021-02-12 08:20:45 +01:00
|
|
|
form.errors["email"][0],
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2016-11-09 00:47:27 +01:00
|
|
|
|
2017-11-23 02:59:51 +01:00
|
|
|
def test_access_signup_page_in_root_domain_without_realm(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_get("/register", subdomain="", follow=True)
|
2017-11-23 02:59:51 +01:00
|
|
|
self.assert_in_success_response(["Find your Zulip accounts"], result)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_ldap_registration_from_confirmation(self) -> None:
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2016-11-02 11:10:21 +01:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
subdomain = "zulip"
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"full_name": "cn"}
|
2016-11-02 11:10:21 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2016-11-02 11:10:21 +01:00
|
|
|
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2016-11-02 11:10:21 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2016-11-02 11:10:21 +01:00
|
|
|
# Visit the confirmation link.
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-11-02 11:10:21 +01:00
|
|
|
for message in reversed(outbox):
|
|
|
|
if email in message.to:
|
2020-07-05 01:38:05 +02:00
|
|
|
match = re.search(settings.EXTERNAL_HOST + r"(\S+)>", message.body)
|
|
|
|
assert match is not None
|
|
|
|
[confirmation_url] = match.groups()
|
2016-11-02 11:10:21 +01:00
|
|
|
break
|
|
|
|
else:
|
2017-03-05 08:01:50 +01:00
|
|
|
raise AssertionError("Couldn't find a confirmation email.")
|
2016-11-02 11:10:21 +01:00
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
2016-11-02 11:10:21 +01:00
|
|
|
result = self.client_get(confirmation_url)
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2017-03-19 01:48:12 +01:00
|
|
|
|
2017-09-28 09:41:19 +02:00
|
|
|
# Full name should be set from LDAP
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Ignore",
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
2023-03-09 12:30:03 +01:00
|
|
|
"Enter your account details to complete registration.",
|
2021-02-12 08:19:30 +01:00
|
|
|
"New LDAP fullname",
|
|
|
|
"newuser@zulip.com",
|
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2017-03-19 01:48:12 +01:00
|
|
|
|
2017-08-09 22:09:38 +02:00
|
|
|
# Verify that the user is asked for name
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_success_response(["id_full_name"], result)
|
2019-01-16 09:56:17 +01:00
|
|
|
# Verify that user is asked for its LDAP/Active Directory password.
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
2021-02-12 08:20:45 +01:00
|
|
|
["Enter your LDAP/Active Directory password.", "ldap-password"], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_not_in_success_response(["id_password"], result)
|
2017-08-09 22:09:38 +02:00
|
|
|
|
2016-11-02 11:10:21 +01:00
|
|
|
# Test the TypeError exception handler
|
2021-02-12 08:19:30 +01:00
|
|
|
with patch(
|
|
|
|
"zproject.backends.ZulipLDAPAuthBackendBase.get_mapped_name", side_effect=TypeError
|
|
|
|
):
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
2021-02-12 08:20:45 +01:00
|
|
|
from_confirmation="1",
|
2021-02-12 08:19:30 +01:00
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
2023-03-09 12:30:03 +01:00
|
|
|
["Enter your account details to complete registration.", "newuser@zulip.com"],
|
|
|
|
result,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.EmailAuthBackend",
|
|
|
|
"zproject.backends.ZulipLDAPUserPopulator",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2019-10-17 23:29:30 +02:00
|
|
|
def test_ldap_populate_only_registration_from_confirmation(self) -> None:
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2019-10-17 23:29:30 +02:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
subdomain = "zulip"
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"full_name": "cn"}
|
2019-10-17 23:29:30 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2019-10-17 23:29:30 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2019-10-17 23:29:30 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2019-10-17 23:29:30 +02:00
|
|
|
# Visit the confirmation link.
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2019-10-17 23:29:30 +02:00
|
|
|
for message in reversed(outbox):
|
|
|
|
if email in message.to:
|
2020-07-05 01:38:05 +02:00
|
|
|
match = re.search(settings.EXTERNAL_HOST + r"(\S+)>", message.body)
|
|
|
|
assert match is not None
|
|
|
|
[confirmation_url] = match.groups()
|
2019-10-17 23:29:30 +02:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise AssertionError("Couldn't find a confirmation email.")
|
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
|
|
|
AUTH_LDAP_BIND_PASSWORD="",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2021-02-12 08:20:45 +01:00
|
|
|
AUTH_LDAP_USER_DN_TEMPLATE="uid=%(user)s,ou=users,dc=zulip,dc=com",
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2019-10-17 23:29:30 +02:00
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
|
|
|
# Full name should be set from LDAP
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Ignore",
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assert_in_success_response(
|
|
|
|
[
|
2023-03-09 12:30:03 +01:00
|
|
|
"Enter your account details to complete registration.",
|
2021-02-12 08:19:30 +01:00
|
|
|
"New LDAP fullname",
|
|
|
|
"newuser@zulip.com",
|
|
|
|
],
|
|
|
|
result,
|
|
|
|
)
|
2019-10-17 23:29:30 +02:00
|
|
|
|
|
|
|
# Verify that the user is asked for name
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_success_response(["id_full_name"], result)
|
2019-10-17 23:29:30 +02:00
|
|
|
# Verify that user is NOT asked for its LDAP/Active Directory password.
|
|
|
|
# LDAP is not configured for authentication in this test.
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_not_in_success_response(
|
2021-02-12 08:20:45 +01:00
|
|
|
["Enter your LDAP/Active Directory password.", "ldap-password"], result
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-10-17 23:29:30 +02:00
|
|
|
# If we were using e.g. the SAML auth backend, there
|
|
|
|
# shouldn't be a password prompt, but since it uses the
|
|
|
|
# EmailAuthBackend, there should be password field here.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_success_response(["id_password"], result)
|
2019-10-17 23:29:30 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_ldap_registration_end_to_end(self) -> None:
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2017-09-29 08:43:18 +02:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
subdomain = "zulip"
|
|
|
|
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"full_name": "cn"}
|
|
|
|
full_name = "New LDAP fullname"
|
2017-09-29 08:43:18 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2017-09-29 08:43:18 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-09-29 08:43:18 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2017-09-29 08:43:18 +02:00
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
2017-09-29 08:43:18 +02:00
|
|
|
# Click confirmation link
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Ignore",
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2017-09-29 08:43:18 +02:00
|
|
|
|
|
|
|
# Full name should be set from LDAP
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
2023-03-09 12:30:03 +01:00
|
|
|
[
|
|
|
|
"Enter your account details to complete registration.",
|
|
|
|
full_name,
|
|
|
|
"newuser@zulip.com",
|
|
|
|
],
|
|
|
|
result,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-09-29 08:43:18 +02:00
|
|
|
|
2017-10-24 19:38:31 +02:00
|
|
|
# Submit the final form with the wrong password.
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
2021-02-12 08:20:45 +01:00
|
|
|
"wrongpassword",
|
2021-02-12 08:19:30 +01:00
|
|
|
full_name=full_name,
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2017-10-24 19:38:31 +02:00
|
|
|
# Didn't create an account
|
|
|
|
with self.assertRaises(UserProfile.DoesNotExist):
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
2017-10-24 19:38:31 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/login/?email=newuser%40zulip.com")
|
2017-10-24 19:38:31 +02:00
|
|
|
|
2019-01-16 09:08:52 +01:00
|
|
|
# Submit the final form with the correct password.
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name=full_name,
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
2017-10-23 21:12:59 +02:00
|
|
|
# Name comes from form which was set by LDAP.
|
|
|
|
self.assertEqual(user_profile.full_name, full_name)
|
2019-01-16 09:08:52 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2019-01-16 09:08:52 +01:00
|
|
|
def test_ldap_split_full_name_mapping(self) -> None:
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"first_name": "sn", "last_name": "cn"}
|
2019-01-16 09:08:52 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
subdomain = "zulip"
|
|
|
|
email = "newuser_splitname@zulip.com"
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser_splitname")
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2019-01-16 09:08:52 +01:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2019-01-16 09:08:52 +01:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2019-01-16 09:08:52 +01:00
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
2019-01-16 09:08:52 +01:00
|
|
|
# Click confirmation link
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Ignore",
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2019-01-16 09:08:52 +01:00
|
|
|
|
|
|
|
# Test split name mapping.
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Ignore",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
2019-01-16 09:08:52 +01:00
|
|
|
# Name comes from form which was set by LDAP.
|
|
|
|
self.assertEqual(user_profile.full_name, "First Last")
|
2017-09-29 08:43:18 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_ldap_auto_registration_on_login(self) -> None:
|
2017-10-23 20:42:37 +02:00
|
|
|
"""The most common way for LDAP authentication to be used is with a
|
|
|
|
server that doesn't have a terms-of-service required, in which
|
|
|
|
case we offer a complete single-sign-on experience (where the
|
|
|
|
user just enters their LDAP username and password, and their
|
|
|
|
account is created if it doesn't already exist).
|
|
|
|
|
|
|
|
This test verifies that flow.
|
|
|
|
"""
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2017-10-23 20:42:37 +02:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
subdomain = "zulip"
|
|
|
|
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2019-01-29 13:39:21 +01:00
|
|
|
ldap_user_attr_map = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "cn",
|
|
|
|
"custom_profile_field__phone_number": "homePhone",
|
2019-01-29 13:39:21 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
full_name = "New LDAP fullname"
|
2017-10-23 20:42:37 +02:00
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
2021-02-12 08:19:30 +01:00
|
|
|
self.login_with_return(email, password, HTTP_HOST=subdomain + ".testserver")
|
2017-10-23 20:42:37 +02:00
|
|
|
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
2017-10-23 20:42:37 +02:00
|
|
|
# Name comes from form which was set by LDAP.
|
|
|
|
self.assertEqual(user_profile.full_name, full_name)
|
2019-01-29 13:39:21 +01:00
|
|
|
|
|
|
|
# Test custom profile fields are properly synced.
|
2021-02-12 08:19:30 +01:00
|
|
|
phone_number_field = CustomProfileField.objects.get(
|
2021-02-12 08:20:45 +01:00
|
|
|
realm=user_profile.realm, name="Phone number"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
phone_number_field_value = CustomProfileFieldValue.objects.get(
|
|
|
|
user_profile=user_profile, field=phone_number_field
|
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(phone_number_field_value.value, "a-new-number")
|
2017-10-23 20:42:37 +02:00
|
|
|
|
2021-06-09 15:39:18 +02:00
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=("zproject.backends.ZulipLDAPAuthBackend",))
|
|
|
|
def test_ldap_auto_registration_on_login_invalid_email_in_directory(self) -> None:
|
|
|
|
password = self.ldap_password("newuser_with_email")
|
|
|
|
username = "newuser_with_email"
|
|
|
|
subdomain = "zulip"
|
|
|
|
|
|
|
|
self.init_default_ldap_database()
|
|
|
|
|
|
|
|
self.change_ldap_user_attr("newuser_with_email", "mail", "thisisnotavalidemail")
|
|
|
|
|
|
|
|
with self.settings(
|
|
|
|
LDAP_EMAIL_ATTR="mail",
|
|
|
|
), self.assertLogs("zulip.auth.ldap", "WARNING") as mock_log:
|
|
|
|
original_user_count = UserProfile.objects.count()
|
|
|
|
self.login_with_return(username, password, HTTP_HOST=subdomain + ".testserver")
|
|
|
|
# Verify that the process failed as intended - no UserProfile is created.
|
|
|
|
self.assertEqual(UserProfile.objects.count(), original_user_count)
|
|
|
|
self.assertEqual(
|
|
|
|
mock_log.output,
|
|
|
|
["WARNING:zulip.auth.ldap:thisisnotavalidemail is not a valid email address."],
|
|
|
|
)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
@override_settings(AUTHENTICATION_BACKENDS=("zproject.backends.ZulipLDAPAuthBackend",))
|
2019-03-04 13:16:00 +01:00
|
|
|
def test_ldap_registration_multiple_realms(self) -> None:
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2019-03-04 13:16:00 +01:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2019-03-04 13:16:00 +01:00
|
|
|
ldap_user_attr_map = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"full_name": "cn",
|
2019-03-04 13:16:00 +01:00
|
|
|
}
|
2021-03-10 20:54:27 +01:00
|
|
|
do_create_realm("test", "test", emails_restricted_to_domains=False)
|
2019-03-04 13:16:00 +01:00
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
2019-03-04 13:16:00 +01:00
|
|
|
subdomain = "zulip"
|
2021-02-12 08:19:30 +01:00
|
|
|
self.login_with_return(email, password, HTTP_HOST=subdomain + ".testserver")
|
2019-03-04 13:16:00 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email, realm=get_realm("zulip"))
|
2019-03-04 13:16:00 +01:00
|
|
|
self.logout()
|
|
|
|
|
|
|
|
# Test registration in another realm works.
|
|
|
|
subdomain = "test"
|
2021-02-12 08:19:30 +01:00
|
|
|
self.login_with_return(email, password, HTTP_HOST=subdomain + ".testserver")
|
2019-03-04 13:16:00 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email, realm=get_realm("test"))
|
2020-03-12 14:17:25 +01:00
|
|
|
self.assertEqual(user_profile.delivery_email, email)
|
2019-03-04 13:16:00 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_ldap_registration_when_names_changes_are_disabled(self) -> None:
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2017-09-29 08:43:18 +02:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
subdomain = "zulip"
|
|
|
|
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"full_name": "cn"}
|
2017-09-29 08:43:18 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2017-09-29 08:43:18 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-09-29 08:43:18 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2017-09-29 08:43:18 +02:00
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
2017-09-29 08:43:18 +02:00
|
|
|
# Click confirmation link. This will 'authenticated_full_name'
|
|
|
|
# session variable which will be used to set the fullname of
|
|
|
|
# the user.
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Ignore",
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2017-09-29 08:43:18 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.name_changes_disabled", return_value=True):
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
2017-09-29 08:43:18 +02:00
|
|
|
# Name comes from LDAP session.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(user_profile.full_name, "New LDAP fullname")
|
2017-09-29 08:43:18 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.EmailAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
def test_signup_with_ldap_and_email_enabled_using_email_with_ldap_append_domain(self) -> None:
|
2019-10-16 18:28:55 +02:00
|
|
|
password = "nonldappassword"
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
subdomain = "zulip"
|
|
|
|
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"full_name": "cn"}
|
2019-01-16 14:09:30 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
|
2019-10-25 02:26:05 +02:00
|
|
|
# If the user's email is inside the LDAP directory and we just
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
# have a wrong password, then we refuse to create an account
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
2021-02-12 08:19:30 +01:00
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Non-LDAP Full Name",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
# We get redirected back to the login page because password was wrong
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/login/?email=newuser%40zulip.com")
|
2020-03-12 14:17:25 +01:00
|
|
|
self.assertFalse(UserProfile.objects.filter(delivery_email=email).exists())
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
|
2019-11-23 18:17:41 +01:00
|
|
|
# For the rest of the test we delete the user from ldap.
|
|
|
|
del self.mock_ldap.directory["uid=newuser,ou=users,dc=zulip,dc=com"]
|
|
|
|
|
|
|
|
# If the user's email is not in the LDAP directory, but fits LDAP_APPEND_DOMAIN,
|
|
|
|
# we refuse to create the account.
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2021-02-12 08:20:45 +01:00
|
|
|
), self.assertLogs("zulip.ldap", "DEBUG") as debug_log:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Non-LDAP Full Name",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2019-11-23 18:17:41 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
# We get redirected back to the login page because emails matching LDAP_APPEND_DOMAIN,
|
2020-10-23 02:43:28 +02:00
|
|
|
# aren't allowed to create non-LDAP accounts.
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/login/?email=newuser%40zulip.com")
|
2020-03-12 14:17:25 +01:00
|
|
|
self.assertFalse(UserProfile.objects.filter(delivery_email=email).exists())
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
debug_log.output,
|
|
|
|
[
|
2021-02-12 08:20:45 +01:00
|
|
|
"DEBUG:zulip.ldap:ZulipLDAPAuthBackend: No LDAP user matching django_to_ldap_username result: newuser. Input username: newuser@zulip.com"
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
)
|
2019-11-23 18:17:41 +01:00
|
|
|
|
2020-10-23 02:43:28 +02:00
|
|
|
# If the email is outside of LDAP_APPEND_DOMAIN, we successfully create a non-LDAP account,
|
|
|
|
# with the password managed in the Zulip database.
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="example.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
2020-12-23 21:45:16 +01:00
|
|
|
with self.assertLogs(level="WARNING") as m:
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
2021-02-12 08:19:30 +01:00
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2020-12-23 21:45:16 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
m.output,
|
2021-02-12 08:20:45 +01:00
|
|
|
["WARNING:root:New account email newuser@zulip.com could not be found in LDAP"],
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
with self.assertLogs("zulip.ldap", "DEBUG") as debug_log:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Non-LDAP Full Name",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
debug_log.output,
|
|
|
|
[
|
2021-02-12 08:20:45 +01:00
|
|
|
"DEBUG:zulip.ldap:ZulipLDAPAuthBackend: Email newuser@zulip.com does not match LDAP domain example.com."
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
)
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "http://zulip.testserver/")
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
# Name comes from the POST request, not LDAP
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(user_profile.full_name, "Non-LDAP Full Name")
|
auth: Improve interactions between LDAPAuthBackend and EmailAuthBackend.
Previously, if you had LDAPAuthBackend enabled, we basically blocked
any other auth backends from working at all, by requiring the user's
login flow include verifying the user's LDAP password.
We still want to enforce that in the case that the account email
matches LDAP_APPEND_DOMAIN, but there's a reasonable corner case:
Having effectively guest users from outside the LDAP domain.
We don't want to allow creating a Zulip-level password for a user
inside the LDAP domain, so we still verify the LDAP password in that
flow, but if the email is allowed to register (due to invite or
whatever) but is outside the LDAP domain for the organization, we
allow it to create an account and set a password.
For the moment, this solution only covers EmailAuthBackend. It's
likely that just extending the list of other backends we check for in
the new conditional on `email_auth_backend` would be correct, but we
haven't done any testing for those cases, and with auth code paths,
it's better to disallow than allow untested code paths.
Fixes #9422.
2018-05-29 06:52:06 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.EmailAuthBackend",
|
|
|
|
"zproject.backends.ZulipDummyBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
def test_signup_with_ldap_and_email_enabled_using_email_with_ldap_email_search(self) -> None:
|
|
|
|
# If the user's email is inside the LDAP directory and we just
|
|
|
|
# have a wrong password, then we refuse to create an account
|
|
|
|
password = "nonldappassword"
|
|
|
|
email = "newuser_email@zulip.com" # belongs to user uid=newuser_with_email in the test directory
|
|
|
|
subdomain = "zulip"
|
|
|
|
|
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"full_name": "cn"}
|
2019-10-25 02:26:05 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2019-10-25 02:26:05 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2019-10-25 02:26:05 +02:00
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_EMAIL_ATTR="mail",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-25 02:26:05 +02:00
|
|
|
):
|
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
2021-02-12 08:19:30 +01:00
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Non-LDAP Full Name",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
# We get redirected back to the login page because password was wrong
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/login/?email=newuser_email%40zulip.com")
|
2020-03-12 14:17:25 +01:00
|
|
|
self.assertFalse(UserProfile.objects.filter(delivery_email=email).exists())
|
2019-10-25 02:26:05 +02:00
|
|
|
|
2023-02-23 01:40:19 +01:00
|
|
|
# If the user's email is not in the LDAP directory, though, we
|
2019-10-25 02:26:05 +02:00
|
|
|
# successfully create an account with a password in the Zulip
|
|
|
|
# database.
|
|
|
|
password = "nonldappassword"
|
|
|
|
email = "nonexistent@zulip.com"
|
|
|
|
subdomain = "zulip"
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2019-10-25 02:26:05 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2019-10-25 02:26:05 +02:00
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_EMAIL_ATTR="mail",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-25 02:26:05 +02:00
|
|
|
):
|
2020-12-23 21:45:16 +01:00
|
|
|
with self.assertLogs(level="WARNING") as m:
|
2019-10-25 02:26:05 +02:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
2021-02-12 08:19:30 +01:00
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
m.output,
|
|
|
|
[
|
2021-02-12 08:20:45 +01:00
|
|
|
"WARNING:root:New account email nonexistent@zulip.com could not be found in LDAP"
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with self.assertLogs("zulip.ldap", "DEBUG") as debug_log:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Non-LDAP Full Name",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
debug_log.output,
|
|
|
|
[
|
2021-02-12 08:20:45 +01:00
|
|
|
"DEBUG:zulip.ldap:ZulipLDAPAuthBackend: No LDAP user matching django_to_ldap_username result: nonexistent@zulip.com. Input username: nonexistent@zulip.com"
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
)
|
2019-10-25 02:26:05 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "http://zulip.testserver/")
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
2019-10-25 02:26:05 +02:00
|
|
|
# Name comes from the POST request, not LDAP
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(user_profile.full_name, "Non-LDAP Full Name")
|
2019-10-25 02:26:05 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
def ldap_invite_and_signup_as(
|
2021-02-12 08:20:45 +01:00
|
|
|
self, invite_as: int, streams: Sequence[str] = ["Denmark"]
|
2021-02-12 08:19:30 +01:00
|
|
|
) -> None:
|
2019-10-16 18:28:55 +02:00
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"full_name": "cn"}
|
2019-01-16 09:59:01 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
subdomain = "zulip"
|
|
|
|
email = "newuser@zulip.com"
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2019-01-16 09:59:01 +01:00
|
|
|
|
|
|
|
with self.settings(
|
2021-02-12 08:19:30 +01:00
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2019-10-16 18:28:55 +02:00
|
|
|
):
|
2021-02-12 08:20:45 +01:00
|
|
|
with self.assertLogs("zulip.ldap", "DEBUG") as debug_log:
|
2020-07-27 03:08:32 +02:00
|
|
|
# Invite user.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.login("iago")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
debug_log.output,
|
|
|
|
[
|
2021-02-12 08:20:45 +01:00
|
|
|
"DEBUG:zulip.ldap:ZulipLDAPAuthBackend: No LDAP user matching django_to_ldap_username result: iago. Input username: iago@zulip.com"
|
2021-02-12 08:19:30 +01:00
|
|
|
],
|
|
|
|
)
|
2023-03-09 02:27:46 +01:00
|
|
|
stream_ids = [self.get_stream_id(stream_name) for stream_name in streams]
|
|
|
|
response = self.client_post(
|
|
|
|
"/json/invites",
|
|
|
|
{
|
|
|
|
"invitee_emails": email,
|
|
|
|
"stream_ids": orjson.dumps(stream_ids).decode(),
|
|
|
|
"invite_as": invite_as,
|
|
|
|
},
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-10-16 18:28:55 +02:00
|
|
|
self.assert_json_success(response)
|
|
|
|
self.logout()
|
2019-01-16 09:59:01 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Ignore",
|
|
|
|
from_confirmation="1",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2019-01-16 09:59:01 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="Ignore",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2019-01-16 09:59:01 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.EmailAuthBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2019-01-16 09:59:01 +01:00
|
|
|
def test_ldap_invite_user_as_admin(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.ldap_invite_and_signup_as(PreregistrationUser.INVITE_AS["REALM_ADMIN"])
|
|
|
|
user_profile = UserProfile.objects.get(delivery_email=self.nonreg_email("newuser"))
|
2019-01-16 09:59:01 +01:00
|
|
|
self.assertTrue(user_profile.is_realm_admin)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.EmailAuthBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2019-01-16 09:59:01 +01:00
|
|
|
def test_ldap_invite_user_as_guest(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.ldap_invite_and_signup_as(PreregistrationUser.INVITE_AS["GUEST_USER"])
|
|
|
|
user_profile = UserProfile.objects.get(delivery_email=self.nonreg_email("newuser"))
|
2019-01-16 09:59:01 +01:00
|
|
|
self.assertTrue(user_profile.is_guest)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
@override_settings(
|
|
|
|
AUTHENTICATION_BACKENDS=(
|
2021-02-12 08:20:45 +01:00
|
|
|
"zproject.backends.ZulipLDAPAuthBackend",
|
|
|
|
"zproject.backends.EmailAuthBackend",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
)
|
2019-01-16 09:59:01 +01:00
|
|
|
def test_ldap_invite_streams(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
stream_name = "Rome"
|
|
|
|
realm = get_realm("zulip")
|
2019-01-16 09:59:01 +01:00
|
|
|
stream = get_stream(stream_name, realm)
|
2021-07-24 16:56:39 +02:00
|
|
|
default_streams = get_default_streams_for_realm(realm.id)
|
2019-01-16 09:59:01 +01:00
|
|
|
default_streams_name = [stream.name for stream in default_streams]
|
|
|
|
self.assertNotIn(stream_name, default_streams_name)
|
|
|
|
|
|
|
|
# Invite user.
|
2021-02-12 08:19:30 +01:00
|
|
|
self.ldap_invite_and_signup_as(
|
2021-02-12 08:20:45 +01:00
|
|
|
PreregistrationUser.INVITE_AS["REALM_ADMIN"], streams=[stream_name]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-01-16 09:59:01 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=self.nonreg_email("newuser"))
|
2019-01-16 09:59:01 +01:00
|
|
|
self.assertTrue(user_profile.is_realm_admin)
|
|
|
|
sub = get_stream_subscriptions_for_user(user_profile).filter(recipient__type_id=stream.id)
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(sub, 1)
|
2019-01-16 09:59:01 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_registration_when_name_changes_are_disabled(self) -> None:
|
2017-09-29 08:43:18 +02:00
|
|
|
"""
|
|
|
|
Test `name_changes_disabled` when we are not running under LDAP.
|
|
|
|
"""
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2017-09-29 08:43:18 +02:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
subdomain = "zulip"
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2017-09-29 08:43:18 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-09-29 08:43:18 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2017-09-29 08:43:18 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.name_changes_disabled", return_value=True):
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
full_name="New Name",
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2020-03-12 14:17:25 +01:00
|
|
|
user_profile = UserProfile.objects.get(delivery_email=email)
|
2017-09-29 08:43:18 +02:00
|
|
|
# 'New Name' comes from POST data; not from LDAP session.
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(user_profile.full_name, "New Name")
|
2017-09-29 08:43:18 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_realm_creation_through_ldap(self) -> None:
|
2020-02-19 19:40:49 +01:00
|
|
|
password = self.ldap_password("newuser")
|
2017-06-15 19:24:46 +02:00
|
|
|
email = "newuser@zulip.com"
|
|
|
|
subdomain = "zulip"
|
|
|
|
realm_name = "Zulip"
|
2019-10-16 18:28:55 +02:00
|
|
|
|
|
|
|
self.init_default_ldap_database()
|
2021-02-12 08:20:45 +01:00
|
|
|
ldap_user_attr_map = {"full_name": "cn"}
|
2017-06-15 19:24:46 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
with patch("zerver.views.registration.get_subdomain", return_value=subdomain):
|
|
|
|
result = self.client_post("/register/", {"email": email})
|
2017-06-15 19:24:46 +02:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-06-15 19:24:46 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2017-06-15 19:24:46 +02:00
|
|
|
# Visit the confirmation link.
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-06-15 19:24:46 +02:00
|
|
|
for message in reversed(outbox):
|
|
|
|
if email in message.to:
|
2020-07-05 01:38:05 +02:00
|
|
|
match = re.search(settings.EXTERNAL_HOST + r"(\S+)>", message.body)
|
|
|
|
assert match is not None
|
|
|
|
[confirmation_url] = match.groups()
|
2017-06-15 19:24:46 +02:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise AssertionError("Couldn't find a confirmation email.")
|
|
|
|
|
|
|
|
with self.settings(
|
|
|
|
POPULATE_PROFILE_VIA_LDAP=True,
|
2021-02-12 08:20:45 +01:00
|
|
|
LDAP_APPEND_DOMAIN="zulip.com",
|
2017-06-15 19:24:46 +02:00
|
|
|
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
|
2021-02-12 08:20:45 +01:00
|
|
|
AUTHENTICATION_BACKENDS=("zproject.backends.ZulipLDAPAuthBackend",),
|
2021-11-03 21:36:54 +01:00
|
|
|
TERMS_OF_SERVICE_VERSION=1.0,
|
2017-06-15 19:24:46 +02:00
|
|
|
):
|
|
|
|
result = self.client_get(confirmation_url)
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2020-04-10 05:28:15 +02:00
|
|
|
key = find_key_by_email(email)
|
|
|
|
confirmation = Confirmation.objects.get(confirmation_key=key)
|
2017-06-15 19:24:46 +02:00
|
|
|
prereg_user = confirmation.content_object
|
2021-07-24 16:56:39 +02:00
|
|
|
assert prereg_user is not None
|
2017-06-15 19:24:46 +02:00
|
|
|
prereg_user.realm_creation = True
|
|
|
|
prereg_user.save()
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
realm_name=realm_name,
|
|
|
|
realm_subdomain=subdomain,
|
2021-02-12 08:20:45 +01:00
|
|
|
from_confirmation="1",
|
2021-02-12 08:19:30 +01:00
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
|
|
|
self.assert_in_success_response(
|
2023-03-09 12:30:03 +01:00
|
|
|
["Enter your account details to complete registration.", "newuser@zulip.com"],
|
|
|
|
result,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
@patch(
|
2021-02-12 08:20:45 +01:00
|
|
|
"DNS.dnslookup",
|
|
|
|
return_value=[["sipbtest:*:20922:101:Fred Sipb,,,:/mit/sipbtest:/bin/athena/tcsh"]],
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_registration_of_mirror_dummy_user(self, ignored: Any) -> None:
|
2016-11-02 17:31:11 +01:00
|
|
|
password = "test"
|
2017-09-16 19:51:26 +02:00
|
|
|
subdomain = "zephyr"
|
2017-05-23 01:27:31 +02:00
|
|
|
user_profile = self.mit_user("sipbtest")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = user_profile.delivery_email
|
2016-11-02 17:31:11 +01:00
|
|
|
user_profile.is_mirror_dummy = True
|
|
|
|
user_profile.save()
|
2021-02-14 00:03:40 +01:00
|
|
|
change_user_is_active(user_profile, False)
|
2016-11-02 17:31:11 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/register/", {"email": email}, subdomain="zephyr")
|
2016-11-02 17:31:11 +01:00
|
|
|
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2023-01-20 08:22:48 +01:00
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].endswith(
|
|
|
|
f"/accounts/send_confirm/?email={urllib.parse.quote(email)}"
|
|
|
|
)
|
|
|
|
)
|
2017-09-16 19:51:26 +02:00
|
|
|
result = self.client_get(result["Location"], subdomain="zephyr")
|
2023-01-19 12:56:45 +01:00
|
|
|
self.assert_in_response("Check your email", result)
|
2016-11-02 17:31:11 +01:00
|
|
|
# Visit the confirmation link.
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2016-11-02 17:31:11 +01:00
|
|
|
for message in reversed(outbox):
|
|
|
|
if email in message.to:
|
2020-07-05 01:38:05 +02:00
|
|
|
match = re.search(settings.EXTERNAL_HOST + r"(\S+)>", message.body)
|
|
|
|
assert match is not None
|
|
|
|
[confirmation_url] = match.groups()
|
2016-11-02 17:31:11 +01:00
|
|
|
break
|
|
|
|
else:
|
2017-03-05 08:01:50 +01:00
|
|
|
raise AssertionError("Couldn't find a confirmation email.")
|
2016-11-02 17:31:11 +01:00
|
|
|
|
2017-09-16 19:51:26 +02:00
|
|
|
result = self.client_get(confirmation_url, subdomain="zephyr")
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2017-03-18 20:36:40 +01:00
|
|
|
|
2017-11-27 00:58:56 +01:00
|
|
|
# If the mirror dummy user is already active, attempting to
|
|
|
|
# submit the registration form should raise an AssertionError
|
|
|
|
# (this is an invalid state, so it's a bug we got here):
|
2021-02-14 00:03:40 +01:00
|
|
|
change_user_is_active(user_profile, True)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
with self.assertRaisesRegex(
|
|
|
|
AssertionError, "Mirror dummy user is already active!"
|
2021-02-12 08:20:45 +01:00
|
|
|
), self.assertLogs("django.request", "ERROR") as error_log:
|
2017-11-27 00:58:56 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
2021-02-12 08:20:45 +01:00
|
|
|
from_confirmation="1",
|
2017-11-27 00:58:56 +01:00
|
|
|
# Pass HTTP_HOST for the target subdomain
|
2021-02-12 08:19:30 +01:00
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
|
|
|
self.assertTrue(
|
2021-02-12 08:20:45 +01:00
|
|
|
"ERROR:django.request:Internal Server Error: /accounts/register/" in error_log.output[0]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
self.assertTrue(
|
|
|
|
'raise AssertionError("Mirror dummy user is already active!' in error_log.output[0]
|
|
|
|
)
|
|
|
|
self.assertTrue(
|
2021-02-12 08:20:45 +01:00
|
|
|
"AssertionError: Mirror dummy user is already active!" in error_log.output[0]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-09-16 19:51:26 +02:00
|
|
|
|
2021-02-14 00:03:40 +01:00
|
|
|
change_user_is_active(user_profile, False)
|
2017-03-18 20:36:40 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
2021-02-12 08:20:45 +01:00
|
|
|
from_confirmation="1",
|
2021-02-12 08:19:30 +01:00
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.submit_reg_form_for_user(
|
|
|
|
email,
|
|
|
|
password,
|
|
|
|
# Pass HTTP_HOST for the target subdomain
|
|
|
|
HTTP_HOST=subdomain + ".testserver",
|
|
|
|
)
|
2016-12-16 02:01:34 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2019-05-26 22:12:46 +02:00
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
2016-11-02 17:31:11 +01:00
|
|
|
|
2021-04-13 19:47:35 +02:00
|
|
|
@patch(
|
|
|
|
"DNS.dnslookup",
|
|
|
|
return_value=[["sipbtest:*:20922:101:Fred Sipb,,,:/mit/sipbtest:/bin/athena/tcsh"]],
|
|
|
|
)
|
|
|
|
def test_registration_of_active_mirror_dummy_user(self, ignored: Any) -> None:
|
2017-03-18 20:36:40 +01:00
|
|
|
"""
|
2017-11-27 00:58:56 +01:00
|
|
|
Trying to activate an already-active mirror dummy user should
|
|
|
|
raise an AssertionError.
|
2017-03-18 20:36:40 +01:00
|
|
|
"""
|
2017-05-23 01:27:31 +02:00
|
|
|
user_profile = self.mit_user("sipbtest")
|
2020-03-12 14:17:25 +01:00
|
|
|
email = user_profile.delivery_email
|
2017-03-18 20:36:40 +01:00
|
|
|
user_profile.is_mirror_dummy = True
|
|
|
|
user_profile.save()
|
2021-02-14 00:03:40 +01:00
|
|
|
change_user_is_active(user_profile, True)
|
2017-03-18 20:36:40 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
with self.assertRaisesRegex(
|
|
|
|
AssertionError, "Mirror dummy user is already active!"
|
2021-02-12 08:20:45 +01:00
|
|
|
), self.assertLogs("django.request", "ERROR") as error_log:
|
|
|
|
self.client_post("/register/", {"email": email}, subdomain="zephyr")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
2021-02-12 08:20:45 +01:00
|
|
|
"ERROR:django.request:Internal Server Error: /register/" in error_log.output[0]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
self.assertTrue(
|
|
|
|
'raise AssertionError("Mirror dummy user is already active!' in error_log.output[0]
|
|
|
|
)
|
|
|
|
self.assertTrue(
|
2021-02-12 08:20:45 +01:00
|
|
|
"AssertionError: Mirror dummy user is already active!" in error_log.output[0]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-03-18 20:36:40 +01:00
|
|
|
|
2021-11-03 21:36:54 +01:00
|
|
|
@override_settings(TERMS_OF_SERVICE_VERSION=None)
|
2019-03-13 18:26:01 +01:00
|
|
|
def test_dev_user_registration(self) -> None:
|
|
|
|
"""Verify that /devtools/register_user creates a new user, logs them
|
|
|
|
in, and redirects to the logged-in app."""
|
|
|
|
count = UserProfile.objects.count()
|
2020-06-13 08:59:37 +02:00
|
|
|
email = f"user-{count}@zulip.com"
|
2019-03-13 18:26:01 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/devtools/register_user/")
|
2019-03-13 18:26:01 +01:00
|
|
|
user_profile = UserProfile.objects.all().order_by("id").last()
|
2021-07-24 16:56:39 +02:00
|
|
|
assert user_profile is not None
|
2019-03-13 18:26:01 +01:00
|
|
|
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2020-03-12 14:17:25 +01:00
|
|
|
self.assertEqual(user_profile.delivery_email, email)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result["Location"], "http://zulip.testserver/")
|
2019-05-26 22:12:46 +02:00
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
2019-03-13 18:26:01 +01:00
|
|
|
|
2021-11-03 21:36:54 +01:00
|
|
|
@override_settings(TERMS_OF_SERVICE_VERSION=None)
|
2019-03-13 18:26:01 +01:00
|
|
|
def test_dev_user_registration_create_realm(self) -> None:
|
|
|
|
count = UserProfile.objects.count()
|
2020-06-13 08:59:37 +02:00
|
|
|
string_id = f"realm-{count}"
|
2019-03-13 18:26:01 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/devtools/register_realm/")
|
2019-03-13 18:26:01 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
2021-02-12 08:20:45 +01:00
|
|
|
result["Location"].startswith(f"http://{string_id}.testserver/accounts/login/subdomain")
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2019-03-13 18:26:01 +01:00
|
|
|
result = self.client_get(result["Location"], subdomain=string_id)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result["Location"], f"http://{string_id}.testserver")
|
2019-03-13 18:26:01 +01:00
|
|
|
|
|
|
|
user_profile = UserProfile.objects.all().order_by("id").last()
|
2021-07-24 16:56:39 +02:00
|
|
|
assert user_profile is not None
|
2019-05-26 22:12:46 +02:00
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
2019-03-13 18:26:01 +01:00
|
|
|
|
2021-11-03 21:36:54 +01:00
|
|
|
@override_settings(TERMS_OF_SERVICE_VERSION=None)
|
2021-08-13 20:37:15 +02:00
|
|
|
def test_dev_user_registration_create_demo_realm(self) -> None:
|
|
|
|
result = self.client_post("/devtools/register_demo_realm/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
|
|
|
|
realm = Realm.objects.latest("date_created")
|
|
|
|
self.assertTrue(
|
|
|
|
result["Location"].startswith(
|
|
|
|
f"http://{realm.string_id}.testserver/accounts/login/subdomain"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
result = self.client_get(result["Location"], subdomain=realm.string_id)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], f"http://{realm.string_id}.testserver")
|
|
|
|
|
|
|
|
user_profile = UserProfile.objects.all().order_by("id").last()
|
|
|
|
assert user_profile is not None
|
|
|
|
self.assert_logged_in_user_id(user_profile.id)
|
|
|
|
|
2021-09-02 01:05:30 +02:00
|
|
|
expected_deletion_date = realm.date_created + datetime.timedelta(
|
|
|
|
days=settings.DEMO_ORG_DEADLINE_DAYS
|
|
|
|
)
|
2021-08-13 20:37:15 +02:00
|
|
|
self.assertEqual(realm.demo_organization_scheduled_deletion_date, expected_deletion_date)
|
|
|
|
|
2021-04-09 23:09:56 +02:00
|
|
|
def test_get_default_language_for_new_user(self) -> None:
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
req = HostRequestMock()
|
|
|
|
req.META["HTTP_ACCEPT_LANGUAGE"] = "de,en"
|
|
|
|
self.assertEqual(get_default_language_for_new_user(req, realm), "de")
|
|
|
|
|
|
|
|
do_set_realm_property(realm, "default_language", "hi", acting_user=None)
|
|
|
|
realm.refresh_from_db()
|
2022-05-12 08:28:00 +02:00
|
|
|
req = HostRequestMock()
|
|
|
|
req.META["HTTP_ACCEPT_LANGUAGE"] = "de,en"
|
2021-04-09 23:09:56 +02:00
|
|
|
self.assertEqual(get_default_language_for_new_user(req, realm), "de")
|
|
|
|
|
2022-05-12 08:28:00 +02:00
|
|
|
req = HostRequestMock()
|
2021-04-09 23:09:56 +02:00
|
|
|
req.META["HTTP_ACCEPT_LANGUAGE"] = ""
|
|
|
|
self.assertEqual(get_default_language_for_new_user(req, realm), "hi")
|
|
|
|
|
2016-10-13 20:09:32 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
class DeactivateUserTest(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_deactivate_user(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2020-03-06 18:40:46 +01:00
|
|
|
email = user.email
|
|
|
|
self.login_user(user)
|
2016-10-13 20:09:32 +02:00
|
|
|
self.assertTrue(user.is_active)
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_delete("/json/users/me")
|
2016-10-13 20:09:32 +02:00
|
|
|
self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("hamlet")
|
2016-10-13 20:09:32 +02:00
|
|
|
self.assertFalse(user.is_active)
|
2020-03-06 18:40:46 +01:00
|
|
|
password = initial_password(email)
|
2020-07-05 01:38:05 +02:00
|
|
|
assert password is not None
|
2020-03-06 18:40:46 +01:00
|
|
|
self.assert_login_failure(email, password=password)
|
2016-10-13 20:09:32 +02:00
|
|
|
|
2020-05-16 21:06:43 +02:00
|
|
|
def test_do_not_deactivate_final_owner(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("desdemona")
|
|
|
|
user_2 = self.example_user("iago")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2016-10-13 20:09:32 +02:00
|
|
|
self.assertTrue(user.is_active)
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_delete("/json/users/me")
|
2020-05-16 21:06:43 +02:00
|
|
|
self.assert_json_error(result, "Cannot deactivate the only organization owner.")
|
2021-02-12 08:20:45 +01:00
|
|
|
user = self.example_user("desdemona")
|
2016-10-13 20:09:32 +02:00
|
|
|
self.assertTrue(user.is_active)
|
2020-05-16 21:06:43 +02:00
|
|
|
self.assertTrue(user.is_realm_owner)
|
2021-03-27 05:13:46 +01:00
|
|
|
do_change_user_role(user_2, UserProfile.ROLE_REALM_OWNER, acting_user=None)
|
2020-05-16 21:06:43 +02:00
|
|
|
self.assertTrue(user_2.is_realm_owner)
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_delete("/json/users/me")
|
2016-10-13 20:09:32 +02:00
|
|
|
self.assert_json_success(result)
|
2021-03-27 05:13:46 +01:00
|
|
|
do_change_user_role(user, UserProfile.ROLE_REALM_OWNER, acting_user=None)
|
2017-03-08 12:38:56 +01:00
|
|
|
|
2018-08-21 08:14:46 +02:00
|
|
|
def test_do_not_deactivate_final_user(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
realm = get_realm("zulip")
|
2021-02-14 00:03:40 +01:00
|
|
|
for user_profile in UserProfile.objects.filter(realm=realm).exclude(
|
|
|
|
role=UserProfile.ROLE_REALM_OWNER
|
|
|
|
):
|
|
|
|
do_deactivate_user(user_profile, acting_user=None)
|
2020-06-01 17:37:35 +02:00
|
|
|
user = self.example_user("desdemona")
|
2020-03-06 18:40:46 +01:00
|
|
|
self.login_user(user)
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_delete("/json/users/me")
|
2018-08-21 08:14:46 +02:00
|
|
|
self.assert_json_error(result, "Cannot deactivate the only user.")
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-03-08 12:38:56 +01:00
|
|
|
class TestLoginPage(ZulipTestCase):
|
2021-02-12 08:20:45 +01:00
|
|
|
@patch("django.http.HttpRequest.get_host")
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_login_page_redirects_for_root_alias(self, mock_get_host: MagicMock) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
mock_get_host.return_value = "www.testserver"
|
2017-08-25 05:18:28 +02:00
|
|
|
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True):
|
2017-03-08 12:38:56 +01:00
|
|
|
result = self.client_get("/en/login/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/go/")
|
2018-08-25 16:21:59 +02:00
|
|
|
|
2020-09-13 00:11:30 +02:00
|
|
|
result = self.client_get("/en/login/", {"next": "/upgrade/"})
|
2018-08-25 16:21:59 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/go/?next=%2Fupgrade%2F")
|
2017-03-08 12:38:56 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
@patch("django.http.HttpRequest.get_host")
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_login_page_redirects_for_root_domain(self, mock_get_host: MagicMock) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
mock_get_host.return_value = "testserver"
|
2017-08-25 05:18:28 +02:00
|
|
|
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True):
|
2017-03-08 12:38:56 +01:00
|
|
|
result = self.client_get("/en/login/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/go/")
|
2018-08-25 16:21:59 +02:00
|
|
|
|
2020-09-13 00:11:30 +02:00
|
|
|
result = self.client_get("/en/login/", {"next": "/upgrade/"})
|
2018-08-25 16:21:59 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/go/?next=%2Fupgrade%2F")
|
2017-03-08 12:38:56 +01:00
|
|
|
|
2022-08-27 19:16:55 +02:00
|
|
|
mock_get_host.return_value = "www.zulip.example.com"
|
2021-02-12 08:19:30 +01:00
|
|
|
with self.settings(
|
|
|
|
ROOT_DOMAIN_LANDING_PAGE=True,
|
2022-08-27 19:16:55 +02:00
|
|
|
EXTERNAL_HOST="www.zulip.example.com",
|
2021-02-12 08:20:45 +01:00
|
|
|
ROOT_SUBDOMAIN_ALIASES=["test"],
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2017-03-08 12:38:56 +01:00
|
|
|
result = self.client_get("/en/login/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/go/")
|
2018-08-25 16:21:59 +02:00
|
|
|
|
2020-09-13 00:11:30 +02:00
|
|
|
result = self.client_get("/en/login/", {"next": "/upgrade/"})
|
2018-08-25 16:21:59 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/go/?next=%2Fupgrade%2F")
|
2017-03-08 12:38:56 +01:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
@patch("django.http.HttpRequest.get_host")
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_login_page_works_without_subdomains(self, mock_get_host: MagicMock) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
mock_get_host.return_value = "www.testserver"
|
|
|
|
with self.settings(ROOT_SUBDOMAIN_ALIASES=["www"]):
|
2017-03-08 12:38:56 +01:00
|
|
|
result = self.client_get("/en/login/")
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
mock_get_host.return_value = "testserver"
|
|
|
|
with self.settings(ROOT_SUBDOMAIN_ALIASES=["www"]):
|
2017-03-08 12:38:56 +01:00
|
|
|
result = self.client_get("/en/login/")
|
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2020-03-31 08:31:22 +02:00
|
|
|
def test_login_page_registration_hint(self) -> None:
|
|
|
|
response = self.client_get("/login/")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_not_in_success_response(
|
|
|
|
["Don't have an account yet? You need to be invited to join this organization."],
|
|
|
|
response,
|
|
|
|
)
|
2020-03-31 08:31:22 +02:00
|
|
|
|
|
|
|
realm = get_realm("zulip")
|
|
|
|
realm.invite_required = True
|
|
|
|
realm.save(update_fields=["invite_required"])
|
|
|
|
response = self.client_get("/login/")
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
["Don't have an account yet? You need to be invited to join this organization."],
|
|
|
|
response,
|
|
|
|
)
|
|
|
|
|
2021-08-23 15:14:05 +02:00
|
|
|
@patch("django.http.HttpRequest.get_host", return_value="auth.testserver")
|
|
|
|
def test_social_auth_subdomain_login_page(self, mock_get_host: MagicMock) -> None:
|
|
|
|
result = self.client_get("http://auth.testserver/login/")
|
|
|
|
self.assertEqual(result.status_code, 400)
|
|
|
|
self.assert_in_response("Authentication subdomain", result)
|
|
|
|
|
|
|
|
zulip_realm = get_realm("zulip")
|
|
|
|
session = self.client.session
|
|
|
|
session["subdomain"] = "zulip"
|
|
|
|
session.save()
|
|
|
|
result = self.client_get("http://auth.testserver/login/")
|
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], zulip_realm.uri)
|
2021-08-23 15:14:05 +02:00
|
|
|
|
|
|
|
session = self.client.session
|
|
|
|
session["subdomain"] = "invalid"
|
|
|
|
session.save()
|
|
|
|
result = self.client_get("http://auth.testserver/login/")
|
|
|
|
self.assertEqual(result.status_code, 400)
|
|
|
|
self.assert_in_response("Authentication subdomain", result)
|
|
|
|
|
2021-08-23 15:14:05 +02:00
|
|
|
def test_login_page_is_deactivated_validation(self) -> None:
|
|
|
|
with patch("zerver.views.auth.logging.info") as mock_info:
|
|
|
|
result = self.client_get("/login/?is_deactivated=invalid_email")
|
|
|
|
mock_info.assert_called_once()
|
|
|
|
self.assert_not_in_success_response(["invalid_email"], result)
|
|
|
|
|
2020-03-31 08:31:22 +02:00
|
|
|
|
2017-03-08 12:38:56 +01:00
|
|
|
class TestFindMyTeam(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_template(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_get("/accounts/find/")
|
2021-08-02 23:20:39 +02:00
|
|
|
self.assertIn("Find your Zulip accounts", result.content.decode())
|
2017-03-08 12:38:56 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_result(self) -> None:
|
2020-08-01 15:25:54 +02:00
|
|
|
# We capitalize a letter in cordelia's email to test that the search is case-insensitive.
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/find/", dict(emails="iago@zulip.com,cordeliA@zulip.com")
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-08-25 08:10:12 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2022-05-29 21:12:13 +02:00
|
|
|
result["Location"], "/accounts/find/?emails=iago%40zulip.com%2CcordeliA%40zulip.com"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2021-08-02 23:20:39 +02:00
|
|
|
content = result.content.decode()
|
2017-03-08 12:38:56 +01:00
|
|
|
self.assertIn("Emails sent! You will only receive emails", content)
|
2020-08-01 15:25:54 +02:00
|
|
|
self.assertIn("iago@zulip.com", content)
|
|
|
|
self.assertIn("cordeliA@zulip.com", content)
|
2017-08-25 08:10:12 +02:00
|
|
|
from django.core.mail import outbox
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2021-09-15 15:29:15 +02:00
|
|
|
self.assert_length(outbox, 2)
|
|
|
|
iago_message = outbox[1]
|
|
|
|
cordelia_message = outbox[0]
|
|
|
|
self.assertIn("Zulip Dev", iago_message.body)
|
|
|
|
self.assertNotIn("Lear & Co", iago_message.body)
|
|
|
|
self.assertIn("Zulip Dev", cordelia_message.body)
|
|
|
|
self.assertIn("Lear & Co", cordelia_message.body)
|
2017-03-08 12:38:56 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_find_team_ignore_invalid_email(self) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
result = self.client_post(
|
2021-02-12 08:20:45 +01:00
|
|
|
"/accounts/find/", dict(emails="iago@zulip.com,invalid_email@zulip.com")
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2017-08-25 08:10:12 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
2022-05-29 21:12:13 +02:00
|
|
|
result["Location"],
|
|
|
|
"/accounts/find/?emails=iago%40zulip.com%2Cinvalid_email%40zulip.com",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2022-05-29 21:12:13 +02:00
|
|
|
result = self.client_get(result["Location"])
|
2021-08-02 23:20:39 +02:00
|
|
|
content = result.content.decode()
|
2017-03-11 20:12:01 +01:00
|
|
|
self.assertIn("Emails sent! You will only receive emails", content)
|
2017-05-25 01:44:04 +02:00
|
|
|
self.assertIn(self.example_email("iago"), content)
|
2017-08-25 08:10:12 +02:00
|
|
|
self.assertIn("invalid_email@", content)
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 1)
|
2017-08-25 08:10:12 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_find_team_reject_invalid_email(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_post("/accounts/find/", dict(emails="invalid_string"))
|
2017-08-25 08:10:12 +02:00
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
self.assertIn(b"Enter a valid email", result.content)
|
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2017-03-11 20:12:01 +01:00
|
|
|
|
2017-08-25 08:30:33 +02:00
|
|
|
# Just for coverage on perhaps-unnecessary validation code.
|
2020-09-13 00:11:30 +02:00
|
|
|
result = self.client_get("/accounts/find/", {"emails": "invalid"})
|
2017-08-25 08:30:33 +02:00
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_find_team_zero_emails(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"emails": ""}
|
|
|
|
result = self.client_post("/accounts/find/", data)
|
2021-08-02 23:20:39 +02:00
|
|
|
self.assertIn("This field is required", result.content.decode())
|
2017-03-08 12:38:56 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2017-08-25 08:10:12 +02:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2017-03-08 12:38:56 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_find_team_one_email(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"emails": self.example_email("hamlet")}
|
|
|
|
result = self.client_post("/accounts/find/", data)
|
2017-03-08 12:38:56 +01:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/find/?emails=hamlet%40zulip.com")
|
2017-08-25 08:10:12 +02:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 1)
|
2017-03-08 12:38:56 +01:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_find_team_deactivated_user(self) -> None:
|
2021-03-27 06:02:12 +01:00
|
|
|
do_deactivate_user(self.example_user("hamlet"), acting_user=None)
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"emails": self.example_email("hamlet")}
|
|
|
|
result = self.client_post("/accounts/find/", data)
|
2017-08-25 08:14:55 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/find/?emails=hamlet%40zulip.com")
|
2017-08-25 08:14:55 +02:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2017-08-25 08:14:55 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_find_team_deactivated_realm(self) -> None:
|
2021-04-02 17:11:25 +02:00
|
|
|
do_deactivate_realm(get_realm("zulip"), acting_user=None)
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"emails": self.example_email("hamlet")}
|
|
|
|
result = self.client_post("/accounts/find/", data)
|
2017-08-25 08:14:55 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/find/?emails=hamlet%40zulip.com")
|
2017-08-25 08:14:55 +02:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2017-08-25 08:14:55 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_find_team_bot_email(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"emails": self.example_email("webhook_bot")}
|
|
|
|
result = self.client_post("/accounts/find/", data)
|
2017-08-25 08:14:55 +02:00
|
|
|
self.assertEqual(result.status_code, 302)
|
2022-05-29 21:12:13 +02:00
|
|
|
self.assertEqual(result["Location"], "/accounts/find/?emails=webhook-bot%40zulip.com")
|
2017-08-25 08:14:55 +02:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2017-08-25 08:14:55 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_find_team_more_than_ten_emails(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
data = {"emails": ",".join(f"hamlet-{i}@zulip.com" for i in range(11))}
|
|
|
|
result = self.client_post("/accounts/find/", data)
|
2017-03-08 12:38:56 +01:00
|
|
|
self.assertEqual(result.status_code, 200)
|
2021-08-02 23:20:39 +02:00
|
|
|
self.assertIn("Please enter at most 10", result.content.decode())
|
2017-08-25 08:10:12 +02:00
|
|
|
from django.core.mail import outbox
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2021-05-17 05:41:32 +02:00
|
|
|
self.assert_length(outbox, 0)
|
2017-03-11 20:12:01 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-03-11 20:12:01 +01:00
|
|
|
class ConfirmationKeyTest(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_confirmation_key(self) -> None:
|
2017-03-11 20:12:01 +01:00
|
|
|
request = MagicMock()
|
|
|
|
request.session = {
|
2021-02-12 08:20:45 +01:00
|
|
|
"confirmation_key": {"confirmation_key": "xyzzy"},
|
2017-03-11 20:12:01 +01:00
|
|
|
}
|
|
|
|
result = confirmation_key(request)
|
|
|
|
self.assert_json_success(result)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assert_in_response("xyzzy", result)
|
2017-04-27 22:58:53 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-04-27 22:58:53 +02:00
|
|
|
class MobileAuthOTPTest(ZulipTestCase):
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_xor_hex_strings(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(xor_hex_strings("1237c81ab", "18989fd12"), "0aaf57cb9")
|
2017-04-27 22:58:53 +02:00
|
|
|
with self.assertRaises(AssertionError):
|
2021-02-12 08:20:45 +01:00
|
|
|
xor_hex_strings("1", "31")
|
2017-04-27 22:58:53 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_is_valid_otp(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(is_valid_otp("1234"), False)
|
|
|
|
self.assertEqual(is_valid_otp("1234abcd" * 8), True)
|
|
|
|
self.assertEqual(is_valid_otp("1234abcZ" * 8), False)
|
2017-04-27 22:58:53 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_ascii_to_hex(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(ascii_to_hex("ZcdR1234"), "5a63645231323334")
|
|
|
|
self.assertEqual(hex_to_ascii("5a63645231323334"), "ZcdR1234")
|
2017-04-27 22:58:53 +02:00
|
|
|
|
2017-11-05 10:51:25 +01:00
|
|
|
def test_otp_encrypt_api_key(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
api_key = "12ac" * 8
|
|
|
|
otp = "7be38894" * 8
|
2018-08-01 11:45:52 +02:00
|
|
|
result = otp_encrypt_api_key(api_key, otp)
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertEqual(result, "4ad1e9f7" * 8)
|
2017-04-27 22:58:53 +02:00
|
|
|
|
2023-04-04 02:43:50 +02:00
|
|
|
decrypted = otp_decrypt_api_key(result, otp)
|
|
|
|
self.assertEqual(decrypted, api_key)
|
2017-04-20 08:25:15 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2018-06-08 11:06:18 +02:00
|
|
|
class NoReplyEmailTest(ZulipTestCase):
|
|
|
|
def test_noreply_email_address(self) -> None:
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertTrue(
|
|
|
|
re.search(self.TOKENIZED_NOREPLY_REGEX, FromAddress.tokenized_no_reply_address())
|
|
|
|
)
|
2018-06-08 11:06:18 +02:00
|
|
|
|
|
|
|
with self.settings(ADD_TOKENS_TO_NOREPLY_ADDRESS=False):
|
|
|
|
self.assertEqual(FromAddress.tokenized_no_reply_address(), "noreply@testserver")
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2017-07-13 13:42:57 +02:00
|
|
|
class TwoFactorAuthTest(ZulipTestCase):
|
2022-07-05 22:14:19 +02:00
|
|
|
@patch("two_factor.plugins.phonenumber.models.totp")
|
python: Convert function type annotations to Python 3 style.
Generated by com2ann (slightly patched to avoid also converting
assignment type annotations, which require Python 3.6), followed by
some manual whitespace adjustment, and six fixes for runtime issues:
- def __init__(self, token: Token, parent: Optional[Node]) -> None:
+ def __init__(self, token: Token, parent: "Optional[Node]") -> None:
-def main(options: argparse.Namespace) -> NoReturn:
+def main(options: argparse.Namespace) -> "NoReturn":
-def fetch_request(url: str, callback: Any, **kwargs: Any) -> Generator[Callable[..., Any], Any, None]:
+def fetch_request(url: str, callback: Any, **kwargs: Any) -> "Generator[Callable[..., Any], Any, None]":
-def assert_server_running(server: subprocess.Popen[bytes], log_file: Optional[str]) -> None:
+def assert_server_running(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> None:
-def server_is_up(server: subprocess.Popen[bytes], log_file: Optional[str]) -> bool:
+def server_is_up(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> bool:
- method_kwarg_pairs: List[FuncKwargPair],
+ method_kwarg_pairs: "List[FuncKwargPair]",
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-19 03:48:37 +02:00
|
|
|
def test_two_factor_login(self, mock_totp: MagicMock) -> None:
|
2017-07-13 13:42:57 +02:00
|
|
|
token = 123456
|
2021-02-12 08:20:45 +01:00
|
|
|
email = self.example_email("hamlet")
|
|
|
|
password = self.ldap_password("hamlet")
|
2017-07-13 13:42:57 +02:00
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
user_profile = self.example_user("hamlet")
|
2017-07-13 13:42:57 +02:00
|
|
|
user_profile.set_password(password)
|
|
|
|
user_profile.save()
|
|
|
|
self.create_default_device(user_profile)
|
|
|
|
|
python: Convert function type annotations to Python 3 style.
Generated by com2ann (slightly patched to avoid also converting
assignment type annotations, which require Python 3.6), followed by
some manual whitespace adjustment, and six fixes for runtime issues:
- def __init__(self, token: Token, parent: Optional[Node]) -> None:
+ def __init__(self, token: Token, parent: "Optional[Node]") -> None:
-def main(options: argparse.Namespace) -> NoReturn:
+def main(options: argparse.Namespace) -> "NoReturn":
-def fetch_request(url: str, callback: Any, **kwargs: Any) -> Generator[Callable[..., Any], Any, None]:
+def fetch_request(url: str, callback: Any, **kwargs: Any) -> "Generator[Callable[..., Any], Any, None]":
-def assert_server_running(server: subprocess.Popen[bytes], log_file: Optional[str]) -> None:
+def assert_server_running(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> None:
-def server_is_up(server: subprocess.Popen[bytes], log_file: Optional[str]) -> bool:
+def server_is_up(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> bool:
- method_kwarg_pairs: List[FuncKwargPair],
+ method_kwarg_pairs: "List[FuncKwargPair]",
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-19 03:48:37 +02:00
|
|
|
def totp(*args: Any, **kwargs: Any) -> int:
|
2017-07-13 13:42:57 +02:00
|
|
|
return token
|
|
|
|
|
|
|
|
mock_totp.side_effect = totp
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
with self.settings(
|
2021-02-12 08:20:45 +01:00
|
|
|
AUTHENTICATION_BACKENDS=("zproject.backends.EmailAuthBackend",),
|
|
|
|
TWO_FACTOR_CALL_GATEWAY="two_factor.gateways.fake.Fake",
|
|
|
|
TWO_FACTOR_SMS_GATEWAY="two_factor.gateways.fake.Fake",
|
2021-02-12 08:19:30 +01:00
|
|
|
TWO_FACTOR_AUTHENTICATION_ENABLED=True,
|
|
|
|
):
|
|
|
|
first_step_data = {
|
|
|
|
"username": email,
|
|
|
|
"password": password,
|
|
|
|
"two_factor_login_view-current_step": "auth",
|
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
with self.assertLogs("two_factor.gateways.fake", "INFO") as info_logs:
|
2020-07-27 03:08:32 +02:00
|
|
|
result = self.client_post("/accounts/login/", first_step_data)
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assertEqual(
|
|
|
|
info_logs.output,
|
|
|
|
['INFO:two_factor.gateways.fake:Fake SMS to +12125550100: "Your token is: 123456"'],
|
|
|
|
)
|
2017-07-13 13:42:57 +02:00
|
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
second_step_data = {
|
|
|
|
"token-otp_token": str(token),
|
|
|
|
"two_factor_login_view-current_step": "token",
|
|
|
|
}
|
2017-07-13 13:42:57 +02:00
|
|
|
result = self.client_post("/accounts/login/", second_step_data)
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "http://zulip.testserver")
|
|
|
|
|
|
|
|
# Going to login page should redirect to '/' if user is already
|
|
|
|
# logged in.
|
2021-02-12 08:20:45 +01:00
|
|
|
result = self.client_get("/accounts/login/")
|
2017-07-13 13:42:57 +02:00
|
|
|
self.assertEqual(result["Location"], "http://zulip.testserver")
|
2018-08-12 03:43:31 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2018-08-12 03:43:31 +02:00
|
|
|
class NameRestrictionsTest(ZulipTestCase):
|
|
|
|
def test_whitelisted_disposable_domains(self) -> None:
|
2021-02-12 08:20:45 +01:00
|
|
|
self.assertFalse(is_disposable_domain("OPayQ.com"))
|
2018-08-25 14:06:17 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2018-08-25 14:06:17 +02:00
|
|
|
class RealmRedirectTest(ZulipTestCase):
|
|
|
|
def test_realm_redirect_without_next_param(self) -> None:
|
|
|
|
result = self.client_get("/accounts/go/")
|
2018-12-06 11:38:20 +01:00
|
|
|
self.assert_in_success_response(["Enter your organization's Zulip URL"], result)
|
2018-08-25 14:06:17 +02:00
|
|
|
|
|
|
|
result = self.client_post("/accounts/go/", {"subdomain": "zephyr"})
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "http://zephyr.testserver")
|
|
|
|
|
|
|
|
result = self.client_post("/accounts/go/", {"subdomain": "invalid"})
|
|
|
|
self.assert_in_success_response(["We couldn't find that Zulip organization."], result)
|
|
|
|
|
|
|
|
def test_realm_redirect_with_next_param(self) -> None:
|
2020-09-13 00:11:30 +02:00
|
|
|
result = self.client_get("/accounts/go/", {"next": "billing"})
|
2021-02-12 08:19:30 +01:00
|
|
|
self.assert_in_success_response(
|
|
|
|
["Enter your organization's Zulip URL", 'action="/accounts/go/?next=billing"'], result
|
|
|
|
)
|
2018-08-25 14:06:17 +02:00
|
|
|
|
|
|
|
result = self.client_post("/accounts/go/?next=billing", {"subdomain": "lear"})
|
|
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
self.assertEqual(result["Location"], "http://lear.testserver/billing")
|