mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 04:53:36 +00:00
Add stricter domain validation and improve error messages.
This commit is contained in:
committed by
Tim Abbott
parent
0ff22f68b3
commit
06cc306d00
@@ -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
21
zerver/lib/domains.py
Normal 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."))
|
||||
@@ -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.")
|
||||
|
||||
@@ -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!")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user