mirror of
https://github.com/zulip/zulip.git
synced 2026-06-12 21:00:58 +08:00
emails: Add backend for disallowing disposable email addresses.
This commit is contained in:
parent
41f8618c04
commit
a44255eedb
@ -1,10 +0,0 @@
|
||||
{% extends "zerver/portico.html" %}
|
||||
{% block portico_content %}
|
||||
|
||||
<h3>{{ _('Private organization') }}</h3>
|
||||
|
||||
<p>{{ _('Hi there! Thank you for your interest in Zulip.') }}</p>
|
||||
|
||||
<p>{% trans %}The organization you are trying to join, {{ closed_domain_name }}, only allows users with e-mail addresses within the organization. Please ask for a new invite to an appropriate e-mail address.{% endtrans %}</p>
|
||||
|
||||
{% endblock %}
|
||||
27
templates/zerver/invalid_email.html
Normal file
27
templates/zerver/invalid_email.html
Normal file
@ -0,0 +1,27 @@
|
||||
{% extends "zerver/portico.html" %}
|
||||
{% block portico_content %}
|
||||
|
||||
<h3>{{ _('Invalid email') }}</h3>
|
||||
|
||||
<p>{{ _('Hi there! Thank you for your interest in Zulip.') }}</p>
|
||||
|
||||
{% if closed_domain %}
|
||||
<p>
|
||||
{% trans %}
|
||||
The organization you are trying to join, {{ realm_name }},
|
||||
only allows users with email addresses within the
|
||||
organization. Please sign up using appropriate email address.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if disposable_emails_not_allowed %}
|
||||
<p>
|
||||
{% trans %}The organization you are trying to join,
|
||||
{{realm_name}}, does not allow signups using disposable email
|
||||
addresses. Please sign up using a real email address.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@ -24,8 +24,8 @@ from zerver.lib.request import JsonableError
|
||||
from zerver.lib.send_email import send_email, FromAddress
|
||||
from zerver.lib.subdomains import get_subdomain, user_matches_subdomain, is_root_domain_available
|
||||
from zerver.lib.users import check_full_name
|
||||
from zerver.models import Realm, get_user, UserProfile, \
|
||||
get_realm, email_to_domain, email_allowed_for_realm
|
||||
from zerver.models import Realm, get_user, UserProfile, get_realm, email_to_domain, \
|
||||
email_allowed_for_realm, disposable_email_check
|
||||
from zproject.backends import email_auth_enabled
|
||||
|
||||
import logging
|
||||
@ -151,6 +151,7 @@ class HomepageForm(forms.Form):
|
||||
"that are allowed to register for accounts in this organization.").format(
|
||||
string_id=realm.string_id, email=email))
|
||||
|
||||
disposable_email_check(realm, email)
|
||||
validate_email_for_realm(realm, email)
|
||||
|
||||
if realm.is_zephyr_mirror_realm:
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.6 on 2018-03-05 19:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('zerver', '0146_userprofile_message_content_in_email_notifications'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='realm',
|
||||
name='disallow_disposable_email_addresses',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
||||
@ -33,6 +33,8 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from zerver.lib import cache
|
||||
from zerver.lib.validator import check_int, check_float, check_string, \
|
||||
check_short_string
|
||||
from zerver.lib.name_restrictions import is_disposable_domain
|
||||
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from bitfield import BitField
|
||||
@ -151,6 +153,7 @@ class Realm(models.Model):
|
||||
show_digest_email = models.BooleanField(default=True) # type: bool
|
||||
name_changes_disabled = models.BooleanField(default=False) # type: bool
|
||||
email_changes_disabled = models.BooleanField(default=False) # type: bool
|
||||
disallow_disposable_email_addresses = models.BooleanField(default=True) # type: bool
|
||||
description = models.TextField(null=True) # type: Optional[Text]
|
||||
send_welcome_emails = models.BooleanField(default=True) # type: bool
|
||||
|
||||
@ -196,6 +199,7 @@ class Realm(models.Model):
|
||||
create_stream_by_admins_only=bool,
|
||||
default_language=Text,
|
||||
description=Text,
|
||||
disallow_disposable_email_addresses=bool,
|
||||
email_changes_disabled=bool,
|
||||
invite_required=bool,
|
||||
invite_by_admins_only=bool,
|
||||
@ -386,6 +390,11 @@ def email_allowed_for_realm(email: Text, realm: Realm) -> bool:
|
||||
return True
|
||||
return False
|
||||
|
||||
def disposable_email_check(realm: Realm, email: Text) -> None:
|
||||
if not realm.restricted_to_domain and realm.disallow_disposable_email_addresses:
|
||||
if is_disposable_domain(email_to_domain(email)):
|
||||
raise ValidationError("Please use your real email address.")
|
||||
|
||||
def get_realm_domains(realm: Realm) -> List[Dict[str, Text]]:
|
||||
return list(realm.realmdomain_set.values('domain', 'allow_subdomains'))
|
||||
|
||||
|
||||
@ -118,6 +118,7 @@ class HomeTest(ZulipTestCase):
|
||||
"realm_default_stream_groups",
|
||||
"realm_default_streams",
|
||||
"realm_description",
|
||||
"realm_disallow_disposable_email_addresses",
|
||||
"realm_domains",
|
||||
"realm_email_auth_enabled",
|
||||
"realm_email_changes_disabled",
|
||||
|
||||
@ -791,7 +791,32 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
|
||||
|
||||
result = self.submit_reg_form_for_user("foo@example.com", "password")
|
||||
self.assertEqual(result.status_code, 200)
|
||||
self.assert_in_response("only allows users with e-mail", result)
|
||||
self.assert_in_response("only allows users with email addresses", result)
|
||||
|
||||
def test_disposable_emails_before_closing(self) -> None:
|
||||
"""
|
||||
If you invite someone with a disposable email when
|
||||
`disallow_disposable_email_addresses = False`, but
|
||||
later changes to true, the invitation should succeed
|
||||
but the invitee's signup attempt should fail.
|
||||
"""
|
||||
zulip_realm = get_realm("zulip")
|
||||
zulip_realm.restricted_to_domain = False
|
||||
zulip_realm.disallow_disposable_email_addresses = False
|
||||
zulip_realm.save()
|
||||
|
||||
self.login(self.example_email("hamlet"))
|
||||
external_address = "foo@mailnator.com"
|
||||
|
||||
self.assert_json_success(self.invite(external_address, ["Denmark"]))
|
||||
self.check_sent_emails([external_address])
|
||||
|
||||
zulip_realm.disallow_disposable_email_addresses = True
|
||||
zulip_realm.save()
|
||||
|
||||
result = self.submit_reg_form_for_user("foo@mailnator.com", "password")
|
||||
self.assertEqual(result.status_code, 200)
|
||||
self.assert_in_response("Please sign up using a real email address.", result)
|
||||
|
||||
def test_invite_with_non_ascii_streams(self) -> None:
|
||||
"""
|
||||
@ -1841,6 +1866,18 @@ class UserSignUpTest(ZulipTestCase):
|
||||
self.assertIn("Your email address, {}, is not in one of the domains".format(email),
|
||||
form.errors['email'][0])
|
||||
|
||||
def test_failed_signup_due_to_disposable_email(self) -> None:
|
||||
realm = get_realm('zulip')
|
||||
realm.restricted_to_domain = False
|
||||
realm.disallow_disposable_email_addresses = True
|
||||
realm.save()
|
||||
|
||||
request = HostRequestMock(host = realm.host)
|
||||
request.session = {} # type: ignore
|
||||
email = 'abc@mailnator.com'
|
||||
form = HomepageForm({'email': email}, realm=realm)
|
||||
self.assertIn("Please use your real email address", form.errors['email'][0])
|
||||
|
||||
def test_failed_signup_due_to_invite_required(self) -> None:
|
||||
realm = get_realm('zulip')
|
||||
realm.invite_required = True
|
||||
|
||||
@ -105,7 +105,7 @@ class TemplateTestCase(ZulipTestCase):
|
||||
'zilencer/enterprise_tos_accept_body.txt',
|
||||
'zerver/zulipchat_migration_tos.html',
|
||||
'zilencer/enterprise_tos_accept_body.txt',
|
||||
'zerver/closed_realm.html',
|
||||
'zerver/invalid_email.html',
|
||||
'zerver/topic_is_muted.html',
|
||||
'zerver/bankruptcy.html',
|
||||
'zerver/lightbox_overlay.html',
|
||||
|
||||
@ -181,6 +181,7 @@ class AdminCreateUserTest(ZulipTestCase):
|
||||
|
||||
admin = self.example_user('hamlet')
|
||||
admin_email = admin.email
|
||||
realm = admin.realm
|
||||
self.login(admin_email)
|
||||
do_change_is_admin(admin, True)
|
||||
|
||||
@ -223,8 +224,6 @@ class AdminCreateUserTest(ZulipTestCase):
|
||||
"Email 'romeo@not-zulip.com' not allowed in this organization")
|
||||
|
||||
RealmDomain.objects.create(realm=get_realm('zulip'), domain='zulip.net')
|
||||
|
||||
# HAPPY PATH STARTS HERE
|
||||
valid_params = dict(
|
||||
email='romeo@zulip.net',
|
||||
password='xxxx',
|
||||
@ -239,12 +238,20 @@ class AdminCreateUserTest(ZulipTestCase):
|
||||
self.assertEqual(new_user.full_name, 'Romeo Montague')
|
||||
self.assertEqual(new_user.short_name, 'Romeo')
|
||||
|
||||
# One more error condition to test--we can't create
|
||||
# the same user twice.
|
||||
# we can't create the same user twice.
|
||||
result = self.client_post("/json/users", valid_params)
|
||||
self.assert_json_error(result,
|
||||
"Email 'romeo@zulip.net' already in use")
|
||||
|
||||
# Don't allow user to sign up with disposable email.
|
||||
realm.restricted_to_domain = False
|
||||
realm.disallow_disposable_email_addresses = True
|
||||
realm.save()
|
||||
|
||||
valid_params["email"] = "abc@mailnator.com"
|
||||
result = self.client_post("/json/users", valid_params)
|
||||
self.assert_json_error(result, "Disposable emails are not allowed for realm 'zulip'")
|
||||
|
||||
class UserProfileTest(ZulipTestCase):
|
||||
def test_get_emails_from_user_ids(self) -> None:
|
||||
hamlet = self.example_user('hamlet')
|
||||
|
||||
@ -30,6 +30,7 @@ def update_realm(
|
||||
name: Optional[str]=REQ(validator=check_string, default=None),
|
||||
description: Optional[str]=REQ(validator=check_string, default=None),
|
||||
restricted_to_domain: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||
disallow_disposable_email_addresses: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||
invite_required: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||
invite_by_admins_only: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||
name_changes_disabled: Optional[bool]=REQ(validator=check_bool, default=None),
|
||||
|
||||
@ -14,7 +14,7 @@ from django.core import validators
|
||||
from zerver.context_processors import get_realm_from_request
|
||||
from zerver.models import UserProfile, Realm, Stream, MultiuseInvite, \
|
||||
name_changes_disabled, email_to_username, email_allowed_for_realm, \
|
||||
get_realm, get_user, get_default_stream_groups
|
||||
get_realm, get_user, get_default_stream_groups, disposable_email_check
|
||||
from zerver.lib.send_email import send_email, FromAddress
|
||||
from zerver.lib.events import do_events_register
|
||||
from zerver.lib.actions import do_change_password, do_change_full_name, do_change_is_admin, \
|
||||
@ -88,8 +88,14 @@ def accounts_register(request: HttpRequest) -> HttpResponse:
|
||||
request, ConfirmationKeyException(ConfirmationKeyException.DOES_NOT_EXIST))
|
||||
|
||||
if not email_allowed_for_realm(email, realm):
|
||||
return render(request, "zerver/closed_realm.html",
|
||||
context={"closed_domain_name": realm.name})
|
||||
return render(request, "zerver/invalid_email.html",
|
||||
context={"realm_name": realm.name, "closed_domain": True})
|
||||
|
||||
try:
|
||||
disposable_email_check(realm, email)
|
||||
except ValidationError:
|
||||
return render(request, "zerver/invalid_email.html",
|
||||
context={"realm_name": realm.name, "disposable_emails_not_allowed": True})
|
||||
|
||||
if realm.deactivated:
|
||||
# The user is trying to register for a deactivated realm. Advise them to
|
||||
|
||||
@ -8,6 +8,7 @@ from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.shortcuts import redirect, render
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from zerver.decorator import require_realm_admin, zulip_login_required
|
||||
from zerver.forms import CreateUserForm
|
||||
@ -30,7 +31,8 @@ from zerver.lib.users import check_valid_bot_type, check_bot_creation_policy, \
|
||||
check_full_name, check_short_name, check_valid_interface_type, check_valid_bot_config
|
||||
from zerver.lib.utils import generate_random_token
|
||||
from zerver.models import UserProfile, Stream, Message, email_allowed_for_realm, \
|
||||
get_user_profile_by_id, get_user, Service, get_user_including_cross_realm
|
||||
get_user_profile_by_id, get_user, Service, get_user_including_cross_realm, \
|
||||
disposable_email_check
|
||||
from zerver.lib.create_user import random_api_key
|
||||
|
||||
|
||||
@ -460,6 +462,11 @@ def create_user_backend(request: HttpRequest, user_profile: UserProfile,
|
||||
return json_error(_("Email '%(email)s' not allowed in this organization") %
|
||||
{'email': email})
|
||||
|
||||
try:
|
||||
disposable_email_check(realm, email)
|
||||
except ValidationError:
|
||||
return json_error(_("Disposable emails are not allowed for realm '{}'".format(realm.string_id)))
|
||||
|
||||
try:
|
||||
get_user(email, user_profile.realm)
|
||||
return json_error(_("Email '%s' already in use") % (email,))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user