diff --git a/frontend_tests/node_tests/templates.js b/frontend_tests/node_tests/templates.js
index 665468a0c4..15f4974c83 100644
--- a/frontend_tests/node_tests/templates.js
+++ b/frontend_tests/node_tests/templates.js
@@ -97,6 +97,7 @@ function render(template_name, args) {
var args = {
alias: {
domain: 'zulip.org',
+ allow_subdomains: true,
},
};
html += render("admin-alias-list", args);
@@ -104,11 +105,15 @@ function render(template_name, args) {
var button = $(html).find('.btn');
var domain = $(html).find('.domain');
+ var row = button.closest('tr');
+ var subdomains_checkbox = row.find('.allow-subdomains');
assert.equal(button.text().trim(), "Remove");
assert(button.hasClass("delete_alias"));
assert.equal(domain.text(), "zulip.org");
+ assert.equal(subdomains_checkbox.prop('checked'), true);
+
global.write_handlebars_output("admin-alias-list", html);
}());
diff --git a/static/js/admin.js b/static/js/admin.js
index e19449a8a2..9cae4caff9 100644
--- a/static/js/admin.js
+++ b/static/js/admin.js
@@ -212,8 +212,8 @@ exports.populate_realm_aliases = function (aliases) {
return;
}
- var domains_list = _.map(page_params.domains, function (ADomain) {
- return ADomain.domain;
+ var domains_list = _.map(aliases, function (alias) {
+ return (alias.allow_subdomains ? "*." + alias.domain : alias.domain);
});
var domains = domains_list.join(', ');
if (domains.length === 0) {
@@ -955,17 +955,19 @@ function _setup_page() {
});
});
- $("#add_alias").click(function () {
+ $("#submit-add-alias").click(function () {
var aliases_info = $("#realm_aliases_modal").find(".aliases_info");
var data = {
- domain: JSON.stringify($("#new_alias").val()),
+ domain: JSON.stringify($("#add-alias-widget .new-alias-domain").val()),
+ allow_subdomains: JSON.stringify($("#add-alias-widget .new-alias-allow-subdomains").prop("checked")),
};
channel.post({
url: "/json/realm/domains",
data: data,
success: function () {
- $("#new_alias").val("");
+ $("#add-alias-widget .new-alias-domain").val("");
+ $("#add-alias-widget .new-alias-allow-subdomains").prop("checked", false);
aliases_info.removeClass("text-error");
aliases_info.addClass("text-success");
aliases_info.text("Added successfully!");
@@ -978,6 +980,38 @@ function _setup_page() {
});
});
+ $("#alias_table").on("change", ".allow-subdomains", function (e) {
+ e.stopPropagation();
+ var aliases_info = $("#realm_aliases_modal").find(".aliases_info");
+ var domain = $(this).parents("tr").find(".domain").text();
+ var allow_subdomains = $(this).prop('checked');
+ var url = '/json/realm/domains/' + domain;
+ var data = {
+ allow_subdomains: JSON.stringify(allow_subdomains),
+ };
+
+ channel.patch({
+ url: url,
+ data: data,
+ success: function () {
+ aliases_info.removeClass("text-error");
+ aliases_info.addClass("text-success");
+ if (allow_subdomains) {
+ aliases_info.text(i18n.t("Update successful: Subdomains allowed for __domain__",
+ {domain: domain}));
+ } else {
+ aliases_info.text(i18n.t("Update successful: Subdomains no longer allowed for __domain__",
+ {domain: domain}));
+ }
+ },
+ error: function (xhr) {
+ aliases_info.removeClass("text-success");
+ aliases_info.addClass("text-error");
+ aliases_info.text(JSON.parse(xhr.responseText).msg);
+ },
+ });
+ });
+
}
exports.setup_page = function () {
diff --git a/static/js/server_events.js b/static/js/server_events.js
index 9f38c8a18c..a6aa6f1e84 100644
--- a/static/js/server_events.js
+++ b/static/js/server_events.js
@@ -108,10 +108,17 @@ function dispatch_normal_event(event) {
break;
case 'realm_domains':
+ var i;
if (event.op === 'add') {
page_params.domains.push(event.alias);
+ } else if (event.op === 'change') {
+ for (i = 0; i < page_params.domains.length; i += 1) {
+ if (page_params.domains[i].domain === event.alias.domain) {
+ page_params.domains[i].allow_subdomains = event.alias.allow_subdomains;
+ break;
+ }
+ }
} else if (event.op === 'remove') {
- var i;
for (i = 0; i < page_params.domains.length; i += 1) {
if (page_params.domains[i].domain === event.domain) {
page_params.domains.splice(i, 1);
@@ -121,6 +128,7 @@ function dispatch_normal_event(event) {
}
admin.populate_realm_aliases(page_params.domains);
break;
+
case 'realm_user':
if (event.op === 'add') {
people.add_in_realm(event.person);
diff --git a/static/styles/settings.css b/static/styles/settings.css
index 29955cd433..888c221c5f 100644
--- a/static/styles/settings.css
+++ b/static/styles/settings.css
@@ -437,12 +437,12 @@ input[type=checkbox].inline-block {
padding: 10px 15px;
}
-#administration #new_alias {
+#administration .new-alias-domain {
width: 130px;
margin-bottom: auto;
padding: 0px 2px;
}
-#administration #add_alias {
+#administration #submit-add-alias {
width: 75px;
}
diff --git a/static/templates/admin_tab.handlebars b/static/templates/admin_tab.handlebars
index 7fdc8cd58e..3199322a8b 100644
--- a/static/templates/admin_tab.handlebars
+++ b/static/templates/admin_tab.handlebars
@@ -83,14 +83,16 @@
diff --git a/static/templates/settings/admin-alias-list.handlebars b/static/templates/settings/admin-alias-list.handlebars
index a6a5607674..c15b142f6a 100644
--- a/static/templates/settings/admin-alias-list.handlebars
+++ b/static/templates/settings/admin-alias-list.handlebars
@@ -1,6 +1,7 @@
{{#with alias}}
| {{domain}} |
+ |
|
{{/with}}
diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py
index b49696c312..be7c76482f 100644
--- a/zerver/lib/actions.py
+++ b/zerver/lib/actions.py
@@ -3616,17 +3616,27 @@ def get_emails_from_user_ids(user_ids):
def get_realm_aliases(realm):
# type: (Realm) -> List[Dict[str, Text]]
- return list(realm.realmalias_set.values('domain'))
+ return list(realm.realmalias_set.values('domain', 'allow_subdomains'))
-def do_add_realm_alias(realm, domain):
- # type: (Realm, Text) -> (RealmAlias)
- alias = RealmAlias.objects.create(realm=realm, domain=domain)
+def do_add_realm_alias(realm, domain, allow_subdomains):
+ # type: (Realm, Text, bool) -> (RealmAlias)
+ alias = RealmAlias.objects.create(realm=realm, domain=domain,
+ allow_subdomains=allow_subdomains)
event = dict(type="realm_domains", op="add",
alias=dict(domain=alias.domain,
- ))
+ allow_subdomains=alias.allow_subdomains))
send_event(event, active_user_ids(realm))
return alias
+def do_change_realm_alias(alias, allow_subdomains):
+ # type: (RealmAlias, bool) -> None
+ alias.allow_subdomains = allow_subdomains
+ alias.save(update_fields=['allow_subdomains'])
+ event = dict(type="realm_domains", op="change",
+ alias=dict(domain=alias.domain,
+ allow_subdomains=alias.allow_subdomains))
+ send_event(event, active_user_ids(alias.realm))
+
def do_remove_realm_alias(alias):
# type: (RealmAlias) -> None
realm = alias.realm
diff --git a/zerver/management/commands/realm_alias.py b/zerver/management/commands/realm_alias.py
index 947b21533b..63ee07e016 100644
--- a/zerver/management/commands/realm_alias.py
+++ b/zerver/management/commands/realm_alias.py
@@ -6,7 +6,10 @@ 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 django.db.utils import IntegrityError
+from django.utils.translation import ugettext as _
+from zerver.models import get_realm, can_add_alias, \
+ Realm, RealmAlias
from zerver.lib.actions import get_realm_aliases
from zerver.lib.domains import validate_domain
import sys
@@ -26,6 +29,11 @@ class Command(BaseCommand):
type=str,
default="show",
help='What operation to do (add, show, remove).')
+ parser.add_argument('--allow-subdomains',
+ dest='allow_subdomains',
+ action="store_true",
+ default=False,
+ help='Whether subdomains are allowed or not.')
parser.add_argument('alias', metavar='', type=str, nargs='?',
help="alias to add or remove")
@@ -35,7 +43,10 @@ class Command(BaseCommand):
if options["op"] == "show":
print("Aliases for %s:" % (realm.domain,))
for alias in get_realm_aliases(realm):
- print(alias["domain"])
+ if alias["allow_subdomains"]:
+ print(alias["domain"] + " (subdomains allowed)")
+ else:
+ print(alias["domain"] + " (subdomains not allowed)")
sys.exit(0)
domain = options['alias'].strip().lower()
@@ -45,14 +56,23 @@ class Command(BaseCommand):
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!")
+ try:
+ if not can_add_alias(domain):
+ print(_("The domain %(domain)s belongs to another organization.") % {'domain': domain})
+ sys.exit(1)
+ RealmAlias.objects.create(realm=realm, domain=domain,
+ allow_subdomains=options["allow_subdomains"])
+ sys.exit(0)
+ except IntegrityError:
+ print(_("The domain %(domain)s is already a part of your organization.") % {'domain': domain})
sys.exit(1)
- RealmAlias.objects.create(realm=realm, domain=domain)
- sys.exit(0)
elif options["op"] == "remove":
- RealmAlias.objects.get(realm=realm, domain=domain).delete()
- sys.exit(0)
+ try:
+ RealmAlias.objects.get(realm=realm, domain=domain).delete()
+ sys.exit(0)
+ except RealmAlias.DoesNotExist:
+ print("No such entry found!")
+ sys.exit(1)
else:
self.print_help("./manage.py", "realm_alias")
sys.exit(1)
diff --git a/zerver/migrations/0051_realmalias_add_allow_subdomains.py b/zerver/migrations/0051_realmalias_add_allow_subdomains.py
new file mode 100644
index 0000000000..14aafa1ed0
--- /dev/null
+++ b/zerver/migrations/0051_realmalias_add_allow_subdomains.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-01-25 20:55
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('zerver', '0050_userprofile_avatar_version'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='realmalias',
+ name='allow_subdomains',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AlterUniqueTogether(
+ name='realmalias',
+ unique_together=set([('realm', 'domain')]),
+ ),
+ ]
diff --git a/zerver/models.py b/zerver/models.py
index 263e0cb9af..e340546637 100644
--- a/zerver/models.py
+++ b/zerver/models.py
@@ -283,6 +283,10 @@ class RealmAlias(models.Model):
realm = models.ForeignKey(Realm, null=True) # type: Optional[Realm]
# should always be stored lowercase
domain = models.CharField(max_length=80, db_index=True) # type: Text
+ allow_subdomains = models.BooleanField(default=False)
+
+ class Meta(object):
+ unique_together = ("realm", "domain")
def can_add_alias(domain):
# type: (Text) -> bool
@@ -315,11 +319,19 @@ def get_realm_by_email_domain(email):
if settings.REALMS_HAVE_SUBDOMAINS:
raise GetRealmByDomainException(
"Cannot get realm from email domain when settings.REALMS_HAVE_SUBDOMAINS = True")
- try:
- alias = RealmAlias.objects.select_related('realm').get(domain = email_to_domain(email))
+ domain = email_to_domain(email)
+ query = RealmAlias.objects.select_related('realm')
+ alias = query.filter(domain=domain).first()
+ if alias is not None:
return alias.realm
- except RealmAlias.DoesNotExist:
- return None
+ else:
+ query = query.filter(allow_subdomains=True)
+ while len(domain) > 0:
+ subdomain, sep, domain = domain.partition('.')
+ alias = query.filter(domain=domain).first()
+ if alias is not None:
+ return alias.realm
+ return None
# Is a user with the given email address allowed to be in the given realm?
# (This function does not check whether the user has been invited to the realm.
@@ -330,11 +342,20 @@ def email_allowed_for_realm(email, realm):
if not realm.restricted_to_domain:
return True
domain = email_to_domain(email)
- return RealmAlias.objects.filter(realm = realm, domain = domain).exists()
+ query = RealmAlias.objects.filter(realm=realm)
+ if query.filter(domain=domain).exists():
+ return True
+ else:
+ query = query.filter(allow_subdomains=True)
+ while len(domain) > 0:
+ subdomain, sep, domain = domain.partition('.')
+ if query.filter(domain=domain).exists():
+ return True
+ return False
def list_of_domains_for_realm(realm):
- # type: (Realm) -> List[Text]
- return list(RealmAlias.objects.filter(realm=realm).values('domain'))
+ # type: (Realm) -> List[Dict[str, Union[str, bool]]]
+ return list(RealmAlias.objects.filter(realm=realm).values('domain', 'allow_subdomains'))
class RealmEmoji(ModelReprMixin, models.Model):
author = models.ForeignKey('UserProfile', blank=True, null=True)
diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py
index c43326cf64..79203255e4 100644
--- a/zerver/tests/test_events.py
+++ b/zerver/tests/test_events.py
@@ -819,10 +819,11 @@ class EventsRegisterTest(ZulipTestCase):
('op', equals('add')),
('alias', check_dict([
('domain', check_string),
+ ('allow_subdomains', check_bool),
])),
])
realm = get_realm('zulip')
- events = self.do_test(lambda: do_add_realm_alias(realm, 'zulip.org'))
+ events = self.do_test(lambda: do_add_realm_alias(realm, 'zulip.org', False))
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
diff --git a/zerver/tests/test_realm_aliases.py b/zerver/tests/test_realm_aliases.py
index ef180276b6..7b8db2c8ff 100644
--- a/zerver/tests/test_realm_aliases.py
+++ b/zerver/tests/test_realm_aliases.py
@@ -1,77 +1,169 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
-from zerver.lib.actions import do_change_is_admin, do_create_realm
+from django.core.exceptions import ValidationError
+from django.db.utils import IntegrityError
+
+from zerver.lib.actions import do_change_is_admin, \
+ do_create_realm, do_change_realm_alias
+from zerver.lib.domains import validate_domain
from zerver.lib.test_classes import ZulipTestCase
-from zerver.models import get_realm, get_realm_by_email_domain, \
- get_user_profile_by_email, GetRealmByDomainException, RealmAlias
+from zerver.models import email_allowed_for_realm, get_realm, \
+ get_realm_by_email_domain, get_user_profile_by_email, \
+ GetRealmByDomainException, RealmAlias
+
import ujson
class RealmAliasTest(ZulipTestCase):
-
- def test_list(self):
+ def test_list_aliases(self):
# type: () -> None
self.login("iago@zulip.com")
realm = get_realm('zulip')
- alias = RealmAlias(realm=realm, domain='zulip.org')
- alias.save()
+ RealmAlias.objects.create(realm=realm, domain='acme.com', allow_subdomains=True)
result = self.client_get("/json/realm/domains")
self.assert_json_success(result)
- self.assertEqual(200, result.status_code)
- content = ujson.loads(result.content)
- self.assertEqual(len(content['domains']), 2)
+ received = ujson.dumps(ujson.loads(result.content)['domains'], sort_keys=True)
+ expected = ujson.dumps([{'domain': 'zulip.com', 'allow_subdomains': False},
+ {'domain': 'acme.com', 'allow_subdomains': True}],
+ sort_keys=True)
+ self.assertEqual(received, expected)
def test_not_realm_admin(self):
# type: () -> None
self.login("hamlet@zulip.com")
result = self.client_post("/json/realm/domains")
self.assert_json_error(result, 'Must be a realm administrator')
+ result = self.client_patch("/json/realm/domains/15")
+ self.assert_json_error(result, 'Must be a realm administrator')
result = self.client_delete("/json/realm/domains/15")
self.assert_json_error(result, 'Must be a realm administrator')
- def test_create(self):
+ def test_create_alias(self):
# type: () -> None
self.login("iago@zulip.com")
- data = {'domain': ujson.dumps('')}
+ data = {'domain': ujson.dumps(''),
+ 'allow_subdomains': ujson.dumps(True)}
result = self.client_post("/json/realm/domains", info=data)
self.assert_json_error(result, 'Invalid domain: Domain can\'t be empty.')
- data = {'domain': ujson.dumps('zulip.org')}
+ data['domain'] = ujson.dumps('acme.com')
result = self.client_post("/json/realm/domains", info=data)
self.assert_json_success(result)
+ realm = get_realm('zulip')
+ self.assertTrue(RealmAlias.objects.filter(realm=realm, domain='acme.com',
+ allow_subdomains=True).exists())
result = self.client_post("/json/realm/domains", info=data)
- self.assert_json_error(result, 'The domain zulip.org is already a part of your organization.')
+ self.assert_json_error(result, 'The domain acme.com is already a part of your organization.')
self.login("sipbtest@mit.edu")
mit_user_profile = get_user_profile_by_email("sipbtest@mit.edu")
do_change_is_admin(mit_user_profile, True)
result = self.client_post("/json/realm/domains", info=data)
- self.assert_json_error(result, 'The domain zulip.org belongs to another organization.')
+ self.assert_json_error(result, 'The domain acme.com belongs to another organization.')
with self.settings(REALMS_HAVE_SUBDOMAINS=True):
result = self.client_post("/json/realm/domains", info=data,
HTTP_HOST=mit_user_profile.realm.host)
self.assert_json_success(result)
- def test_delete(self):
+ def test_patch_alias(self):
# type: () -> None
self.login("iago@zulip.com")
realm = get_realm('zulip')
- RealmAlias.objects.create(realm=realm, domain='zulip.org')
- aliases_count = RealmAlias.objects.count()
+ RealmAlias.objects.create(realm=realm, domain='acme.com',
+ allow_subdomains=False)
+ data = {
+ 'allow_subdomains': ujson.dumps(True),
+ }
+ url = "/json/realm/domains/acme.com"
+ result = self.client_patch(url, data)
+ self.assert_json_success(result)
+ self.assertTrue(RealmAlias.objects.filter(realm=realm, domain='acme.com',
+ allow_subdomains=True).exists())
+
+ url = "/json/realm/domains/non-existent.com"
+ result = self.client_patch(url, data)
+ self.assertEqual(result.status_code, 400)
+ self.assert_json_error(result, 'No entry found for domain non-existent.com.')
+
+ def test_delete_alias(self):
+ # type: () -> None
+ self.login("iago@zulip.com")
+ realm = get_realm('zulip')
+ RealmAlias.objects.create(realm=realm, domain='acme.com')
result = self.client_delete("/json/realm/domains/non-existent.com")
self.assertEqual(result.status_code, 400)
self.assert_json_error(result, 'No entry found for domain non-existent.com.')
- result = self.client_delete("/json/realm/domains/zulip.org")
+ result = self.client_delete("/json/realm/domains/acme.com")
self.assert_json_success(result)
- self.assertEqual(RealmAlias.objects.count(), aliases_count - 1)
+ self.assertFalse(RealmAlias.objects.filter(domain='acme.com').exists())
def test_get_realm_by_email_domain(self):
# type: () -> None
+ realm1, created = do_create_realm('testrealm1', 'Test Realm 1')
+ realm2, created = do_create_realm('testrealm2', 'Test Realm 2')
+ realm3, created = do_create_realm('testrealm3', 'Test Realm 3')
+
+ alias1 = RealmAlias.objects.create(realm=realm1, domain='test1.com', allow_subdomains=True)
+ alias2 = RealmAlias.objects.create(realm=realm2, domain='test2.test1.com', allow_subdomains=False)
+ RealmAlias.objects.create(realm=realm3, domain='test3.test2.test1.com', allow_subdomains=True)
+
self.assertEqual(get_realm_by_email_domain('user@zulip.com').string_id, 'zulip')
self.assertEqual(get_realm_by_email_domain('user@fakedomain.com'), None)
+ self.assertEqual(get_realm_by_email_domain('user@test1.com').string_id, 'testrealm1')
+ self.assertEqual(get_realm_by_email_domain('user@test2.test1.com').string_id, 'testrealm2')
+ self.assertEqual(get_realm_by_email_domain('user@test3.test2.test1.com').string_id, 'testrealm3')
+ self.assertEqual(get_realm_by_email_domain('user@test2.test1.com').string_id, 'testrealm2')
+ self.assertEqual(get_realm_by_email_domain('user@test2.test2.test1.com').string_id, 'testrealm1')
+ self.assertEqual(get_realm_by_email_domain('user@test1.test3.test2.test1.com').string_id, 'testrealm3')
+
+ do_change_realm_alias(alias1, False)
+ self.assertEqual(get_realm_by_email_domain('user@test1.test1.com'), None)
+ self.assertEqual(get_realm_by_email_domain('user@test1.com').string_id, 'testrealm1')
+
+ do_change_realm_alias(alias2, True)
+ self.assertEqual(get_realm_by_email_domain('user@test2.test1.com').string_id, 'testrealm2')
+ self.assertEqual(get_realm_by_email_domain('user@test2.test2.test1.com').string_id, 'testrealm2')
+
with self.settings(REALMS_HAVE_SUBDOMAINS = True), (
self.assertRaises(GetRealmByDomainException)):
get_realm_by_email_domain('user@zulip.com')
+
+ def test_email_allowed_for_realm(self):
+ # type: () -> None
+ realm1, created = do_create_realm('testrealm1', 'Test Realm 1', restricted_to_domain=True)
+ realm2, created = do_create_realm('testrealm2', 'Test Realm 2', restricted_to_domain=True)
+
+ alias = RealmAlias.objects.create(realm=realm1, domain='test1.com', allow_subdomains=False)
+ RealmAlias.objects.create(realm=realm2, domain='test2.test1.com', allow_subdomains=True)
+
+ self.assertEqual(email_allowed_for_realm('user@test1.com', realm1), True)
+ self.assertEqual(email_allowed_for_realm('user@test2.test1.com', realm1), False)
+ self.assertEqual(email_allowed_for_realm('user@test2.test1.com', realm2), True)
+ self.assertEqual(email_allowed_for_realm('user@test3.test2.test1.com', realm2), True)
+ self.assertEqual(email_allowed_for_realm('user@test3.test1.com', realm2), False)
+
+ do_change_realm_alias(alias, True)
+ self.assertEqual(email_allowed_for_realm('user@test1.com', realm1), True)
+ self.assertEqual(email_allowed_for_realm('user@test2.test1.com', realm1), True)
+ self.assertEqual(email_allowed_for_realm('user@test2.com', realm1), False)
+
+ def test_realm_aliases_uniqueness(self):
+ # type: () -> None
+ realm = get_realm('zulip')
+ with self.settings(REALMS_HAVE_SUBDOMAINS=True), self.assertRaises(IntegrityError):
+ RealmAlias.objects.create(realm=realm, domain='zulip.com', allow_subdomains=True)
+
+ def test_validate_domain(self):
+ # type: () -> None
+ invalid_domains = ['', 'test', 't.', 'test.', '.com', '-test', 'test...com',
+ 'test-', 'test_domain.com', 'test.-domain.com']
+ for domain in invalid_domains:
+ with self.assertRaises(ValidationError):
+ validate_domain(domain)
+
+ valid_domains = ['acme.com', 'x-x.y.3.z']
+ for domain in valid_domains:
+ validate_domain(domain)
diff --git a/zerver/views/messages.py b/zerver/views/messages.py
index a26cc8326f..80345a3f97 100644
--- a/zerver/views/messages.py
+++ b/zerver/views/messages.py
@@ -766,6 +766,8 @@ def same_realm_zephyr_user(user_profile, email):
domain = email_to_domain(email)
+ # Assumes allow_subdomains=False for all RealmAlias's corresponding to
+ # these realms.
return user_profile.realm.is_zephyr_mirror_realm and \
RealmAlias.objects.filter(realm=user_profile.realm, domain=domain).exists()
@@ -781,6 +783,8 @@ def same_realm_irc_user(user_profile, email):
domain = email_to_domain(email).replace("irc.", "")
+ # Assumes allow_subdomains=False for all RealmAlias's corresponding to
+ # these realms.
return RealmAlias.objects.filter(realm=user_profile.realm, domain=domain).exists()
def same_realm_jabber_user(user_profile, email):
@@ -794,6 +798,8 @@ def same_realm_jabber_user(user_profile, email):
# Zulip users, this is where you would do any translation.
domain = email_to_domain(email)
+ # Assumes allow_subdomains=False for all RealmAlias's corresponding to
+ # these realms.
return RealmAlias.objects.filter(realm=user_profile.realm, domain=domain).exists()
# We do not @require_login for send_message_backend, since it is used
diff --git a/zerver/views/realm_aliases.py b/zerver/views/realm_aliases.py
index 73529017ad..976354ad2f 100644
--- a/zerver/views/realm_aliases.py
+++ b/zerver/views/realm_aliases.py
@@ -4,12 +4,12 @@ from django.core.exceptions import ValidationError
from django.http import HttpRequest, HttpResponse
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.decorator import has_request_variables, require_realm_admin, REQ
+from zerver.lib.actions import do_add_realm_alias, do_change_realm_alias, \
+ do_remove_realm_alias, get_realm_aliases
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.lib.validator import check_bool, check_string
from zerver.models import can_add_alias, RealmAlias, UserProfile
from typing import Text
@@ -21,8 +21,8 @@ def list_aliases(request, user_profile):
@require_realm_admin
@has_request_variables
-def create_alias(request, user_profile, domain=REQ(validator=check_string)):
- # type: (HttpRequest, UserProfile, Text) -> (HttpResponse)
+def create_alias(request, user_profile, domain=REQ(validator=check_string), allow_subdomains=REQ(validator=check_bool)):
+ # type: (HttpRequest, UserProfile, Text, bool) -> (HttpResponse)
domain = domain.strip().lower()
try:
validate_domain(domain)
@@ -32,9 +32,20 @@ def create_alias(request, user_profile, domain=REQ(validator=check_string)):
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)
+ alias = do_add_realm_alias(user_profile.realm, domain, allow_subdomains)
return json_success({'new_domain': [alias.id, alias.domain]})
+@require_realm_admin
+@has_request_variables
+def patch_alias(request, user_profile, domain, allow_subdomains=REQ(validator=check_bool)):
+ # type: (HttpRequest, UserProfile, Text, bool) -> (HttpResponse)
+ try:
+ alias = RealmAlias.objects.get(realm=user_profile.realm, domain=domain)
+ do_change_realm_alias(alias, allow_subdomains)
+ except RealmAlias.DoesNotExist:
+ return json_error(_('No entry found for domain %(domain)s.' % {'domain': domain}))
+ return json_success()
+
@require_realm_admin
@has_request_variables
def delete_alias(request, user_profile, domain):
diff --git a/zproject/urls.py b/zproject/urls.py
index 2148f55d6d..2cbc714d8f 100644
--- a/zproject/urls.py
+++ b/zproject/urls.py
@@ -165,12 +165,13 @@ v1_api_and_json_patterns = [
# Returns a 204, used by desktop app to verify connectivity status
url(r'generate_204$', zerver.views.registration.generate_204, name='zerver.views.registration.generate_204'),
- # realm/aliases -> zerver.views.realm_aliases
+ # realm/domains -> zerver.views.realm_aliases
url(r'^realm/domains$', rest_dispatch,
{'GET': 'zerver.views.realm_aliases.list_aliases',
'POST': 'zerver.views.realm_aliases.create_alias'}),
url(r'^realm/domains/(?P\S+)$', rest_dispatch,
- {'DELETE': 'zerver.views.realm_aliases.delete_alias'}),
+ {'PATCH': 'zerver.views.realm_aliases.patch_alias',
+ 'DELETE': 'zerver.views.realm_aliases.delete_alias'}),
# realm/emoji -> zerver.views.realm_emoji
url(r'^realm/emoji$', rest_dispatch,