Add stricter domain validation and improve error messages.

This commit is contained in:
Harshit Bansal
2017-01-20 23:27:36 -08:00
committed by Tim Abbott
parent 0ff22f68b3
commit 06cc306d00
6 changed files with 55 additions and 34 deletions

View File

@@ -923,7 +923,7 @@ function _setup_page() {
$("#add_alias").click(function () {
var aliases_info = $("#realm_aliases_modal").find(".aliases_info");
var data = {
domain: $("#new_alias").val(),
domain: JSON.stringify($("#new_alias").val()),
};
channel.post({

21
zerver/lib/domains.py Normal file
View File

@@ -0,0 +1,21 @@
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _
import re
from typing import Text
def validate_domain(domain):
# type: (Text) -> None
if domain is None or len(domain) == 0:
raise ValidationError(_("Domain can't be empty."))
if '.' not in domain:
raise ValidationError(_("Domain must have at least one dot (.)"))
if domain[0] == '.' or domain[-1] == '.':
raise ValidationError(_("Domain cannot start or end with a dot (.)"))
for subdomain in domain.split('.'):
if not subdomain:
raise ValidationError(_("Consecutive '.' are not allowed."))
if subdomain[0] == '-' or subdomain[-1] == '-':
raise ValidationError(_("Subdomains cannot start or end with a '-'."))
if not re.match('^[a-z0-9-]*$', subdomain):
raise ValidationError(_("Domain can only have letters, numbers, '.' and '-'s."))

View File

@@ -5,8 +5,10 @@ from optparse import make_option
from typing import Any, Text
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.management.base import BaseCommand, CommandParser
from zerver.lib.actions import Realm, do_create_realm, set_default_streams
from zerver.lib.domains import validate_domain
from zerver.models import RealmAlias, can_add_alias, get_realm
if settings.ZILENCER_ENABLED:
@@ -59,28 +61,11 @@ Usage: ./manage.py create_realm --string_id=acme --name='Acme'"""
help='Optionally, the ID of the deployment you '
'want to associate the realm with.')
def validate_domain(self, domain):
# type: (str) -> None
# Domains can't contain whitespace if they are to be used in memcached
# keys. Seems safer to leave that as the default case regardless of
# which backing store we use.
if re.search("\s", domain):
raise ValueError("Domains can't contain whitespace")
# Domains must look like domains, ie have the structure of
# <subdomain(s)>.<tld>. One reason for this is that bots need
# to have valid looking emails.
if len(domain.split(".")) < 2:
raise ValueError("Domains must contain a '.'")
if not can_add_alias(domain):
raise ValueError("Domain already assigned to an existing realm")
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
string_id = options["string_id"]
name = options["name"]
domain = options["domain"]
domain = options["domain"].lower()
if not name or not string_id:
print("\033[1;31mPlease provide a name and string_id.\033[0m\n", file=sys.stderr)
@@ -91,8 +76,11 @@ Usage: ./manage.py create_realm --string_id=acme --name='Acme'"""
print("\033[1;31mExternal deployments are not supported on voyager deployments.\033[0m\n", file=sys.stderr)
exit(1)
if domain is not None:
self.validate_domain(domain)
try:
validate_domain(domain)
except ValidationError as e:
print(e.messages[0])
sys.exit(1)
if get_realm(string_id) is not None:
raise ValueError("string_id taken. Please choose another one.")

View File

@@ -4,9 +4,11 @@ from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.exceptions import ValidationError
from django.core.management.base import BaseCommand
from zerver.models import Realm, RealmAlias, get_realm, can_add_alias
from zerver.lib.actions import get_realm_aliases
from zerver.lib.domains import validate_domain
import sys
class Command(BaseCommand):
@@ -36,7 +38,12 @@ class Command(BaseCommand):
print(alias["domain"])
sys.exit(0)
domain = options['alias'].lower()
domain = options['alias'].strip().lower()
try:
validate_domain(domain)
except ValidationError as e:
print(e.messages[0])
sys.exit(1)
if options["op"] == "add":
if not can_add_alias(domain):
print("A Realm already exists for this domain, cannot add it as an alias for another realm!")

View File

@@ -32,16 +32,16 @@ class RealmAliasTest(ZulipTestCase):
def test_create(self):
# type: () -> None
self.login("iago@zulip.com")
data = {"domain": ""}
data = {'domain': ujson.dumps('')}
result = self.client_post("/json/realm/domains", info=data)
self.assert_json_error(result, 'Domain can\'t be empty.')
self.assert_json_error(result, 'Invalid domain: Domain can\'t be empty.')
data['domain'] = 'zulip.org'
data = {'domain': ujson.dumps('zulip.org')}
result = self.client_post("/json/realm/domains", info=data)
self.assert_json_success(result)
result = self.client_post("/json/realm/domains", info=data)
self.assert_json_error(result, 'A Realm for this domain already exists.')
self.assert_json_error(result, 'The domain zulip.org is already a part of your organization.')
def test_delete(self):
# type: () -> None

View File

@@ -7,7 +7,9 @@ from django.utils.translation import ugettext as _
from zerver.decorator import has_request_variables, REQ, require_realm_admin
from zerver.lib.actions import get_realm_aliases, do_add_realm_alias, \
do_remove_realm_alias
from zerver.lib.domains import validate_domain
from zerver.lib.response import json_error, json_success
from zerver.lib.validator import check_string
from zerver.models import can_add_alias, RealmAlias, UserProfile
from typing import Text
@@ -19,15 +21,18 @@ def list_aliases(request, user_profile):
@require_realm_admin
@has_request_variables
def create_alias(request, user_profile, domain=REQ()):
def create_alias(request, user_profile, domain=REQ(validator=check_string)):
# type: (HttpRequest, UserProfile, Text) -> (HttpResponse)
if can_add_alias(domain):
try:
alias = do_add_realm_alias(user_profile.realm, domain)
except ValidationError:
return json_error(_('Domain can\'t be empty.'))
else:
return json_error(_('A Realm for this domain already exists.'))
domain = domain.strip().lower()
try:
validate_domain(domain)
except ValidationError as e:
return json_error(_('Invalid domain: {}').format(e.messages[0]))
if RealmAlias.objects.filter(realm=user_profile.realm, domain=domain).exists():
return json_error(_("The domain %(domain)s is already a part of your organization.") % {'domain': domain})
if not can_add_alias(domain):
return json_error(_("The domain %(domain)s belongs to another organization.") % {'domain': domain})
alias = do_add_realm_alias(user_profile.realm, domain)
return json_success({'new_domain': [alias.id, alias.domain]})
@require_realm_admin