[schema] Allow a realm to have aliases for multiple other domains

(imported from commit 5d0340afd4c77f50db63550a3ec8d81e885c285c)
This commit is contained in:
Leo Franchi
2013-11-07 14:17:54 -05:00
parent 761c0a0266
commit 62ba16a829
6 changed files with 309 additions and 7 deletions

View File

@@ -6,8 +6,8 @@ from django.utils.safestring import mark_safe
from django.contrib.auth.forms import SetPasswordForm
from zerver.models import Realm, get_user_profile_by_email, UserProfile, \
completely_open
from zerver.lib.actions import do_change_password
completely_open, email_to_domain, get_realm
from zerver.lib.actions import do_change_password, alias_for_realm
import DNS
def is_inactive(value):
@@ -20,7 +20,7 @@ def is_inactive(value):
SIGNUP_STRING = '<a href="https://zulip.com/signup">Sign up</a> to find out when Zulip is ready for you.'
def has_valid_realm(value):
return Realm.objects.filter(domain=value.split("@")[-1]).exists()
return Realm.objects.filter(domain=email_to_domain(value)).exists()
def not_mit_mailing_list(value):
# I don't want ec-discuss signed up for Zulip
@@ -62,6 +62,8 @@ class HomepageForm(forms.Form):
data = self.cleaned_data['email']
if completely_open(self.domain) or has_valid_realm(data) and not_mit_mailing_list(data):
return data
if alias_for_realm(email_to_domain(data)) is not None:
return data
raise ValidationError(mark_safe(
u'Registration is not currently available for your domain. ' \
+ SIGNUP_STRING))

View File

@@ -12,7 +12,7 @@ from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity,
to_dict_cache_key, get_realm, stringify_message_dict, bulk_get_recipients, \
email_to_domain, email_to_username, display_recipient_cache_key, \
get_stream_cache_key, to_dict_cache_key_id, is_super_user, \
UserActivityInterval, get_active_user_dicts_in_realm
UserActivityInterval, get_active_user_dicts_in_realm, RealmAlias
from django.db import transaction, IntegrityError
from django.db.models import F, Q
@@ -1987,8 +1987,9 @@ def do_invite_users(user_profile, invitee_emails, streams):
errors.append((email, "Invalid address."))
continue
if user_profile.realm.restricted_to_domain and \
email_to_domain(email).lower() != user_profile.realm.domain.lower():
domains = [user_profile.realm.domain.lower()]
domains.extend(realm_aliases(user_profile.realm))
if user_profile.realm.restricted_to_domain and email_to_domain(email).lower() not in domains:
errors.append((email, "Outside your domain."))
continue
@@ -2181,3 +2182,11 @@ def send_local_email_template_with_delay(recipients, template_prefix,
sender=sender,
tags=tags)
def realm_aliases(realm):
return [alias.domain for alias in realm.realmalias_set.all()]
def alias_for_realm(domain):
try:
return RealmAlias.objects.get(domain=domain)
except RealmAlias.DoesNotExist:
return None

View File

@@ -0,0 +1,56 @@
from __future__ import absolute_import
from optparse import make_option
from django.core.management.base import BaseCommand
from zerver.models import Realm, RealmAlias, get_realm
from zerver.lib.actions import realm_aliases
import sys
class Command(BaseCommand):
help = """Manage aliases for the specified realm
Usage: python manage.py realm_alias --realm=foo.com --op=[add|remove|show] bar.com
"""
option_list = BaseCommand.option_list + (
make_option('-r', '--realm',
dest='domain',
type='str',
help='The name of the realm.'),
make_option('--op',
dest='op',
type='str',
default="show",
help='What operation to do (add, show, remove).'),
)
def handle(self, *args, **options):
if "domain" not in options or options['domain'] is None:
self.print_help("python manage.py", "realm_alias")
sys.exit(1)
realm = Realm.objects.get(domain=options["domain"])
if options["op"] == "show":
print "Aliases for %s:" % (realm.domain,)
for alias in realm_aliases(realm):
print alias
sys.exit(0)
if not args:
self.print_help("python manage.py", "realm_alias")
sys.exit(1)
alias = args[0]
if options["op"] == "add":
if get_realm(alias) is not None:
print "A Realm already exists for this domain, cannot add it as an alias for another realm!"
sys.exit(1)
RealmAlias.objects.create(realm=realm, domain=alias)
sys.exit(0)
elif options["op"] == "remove":
RealmAlias.objects.get(realm=realm, domain=alias).delete()
sys.exit(0)
else:
self.print_help("python manage.py", "realm_alias")
sys.exit(1)

View File

@@ -0,0 +1,227 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'RealmAlias'
db.create_table(u'zerver_realmalias', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('realm', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['zerver.Realm'], null=True)),
('domain', self.gf('django.db.models.fields.CharField')(unique=True, max_length=80, db_index=True)),
))
db.send_create_signal(u'zerver', ['RealmAlias'])
def backwards(self, orm):
# Deleting model 'RealmAlias'
db.delete_table(u'zerver_realmalias')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'zerver.appledevicetoken': {
'Meta': {'object_name': 'AppleDeviceToken'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_updated': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'auto_now': 'True', 'blank': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.client': {
'Meta': {'object_name': 'Client'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30', 'db_index': 'True'})
},
u'zerver.defaultstream': {
'Meta': {'unique_together': "(('realm', 'stream'),)", 'object_name': 'DefaultStream'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"}),
'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Stream']"})
},
u'zerver.huddle': {
'Meta': {'object_name': 'Huddle'},
'huddle_hash': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'zerver.message': {
'Meta': {'object_name': 'Message'},
'content': ('django.db.models.fields.TextField', [], {}),
'edit_history': ('django.db.models.fields.TextField', [], {'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_edit_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'pub_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Recipient']"}),
'rendered_content': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'rendered_content_version': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"}),
'sending_client': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Client']"}),
'subject': ('django.db.models.fields.CharField', [], {'max_length': '60', 'db_index': 'True'})
},
u'zerver.mituser': {
'Meta': {'object_name': 'MitUser'},
'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
u'zerver.preregistrationuser': {
'Meta': {'object_name': 'PreregistrationUser'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invited_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']", 'null': 'True'}),
'referred_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']", 'null': 'True'}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['zerver.Stream']", 'null': 'True', 'symmetrical': 'False'})
},
u'zerver.realm': {
'Meta': {'object_name': 'Realm'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True'}),
'notifications_stream': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': u"orm['zerver.Stream']"}),
'restricted_to_domain': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
},
u'zerver.realmalias': {
'Meta': {'object_name': 'RealmAlias'},
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80', 'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']", 'null': 'True'})
},
u'zerver.realmemoji': {
'Meta': {'unique_together': "(('realm', 'name'),)", 'object_name': 'RealmEmoji'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'img_url': ('django.db.models.fields.TextField', [], {}),
'name': ('django.db.models.fields.TextField', [], {}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"})
},
u'zerver.recipient': {
'Meta': {'unique_together': "(('type', 'type_id'),)", 'object_name': 'Recipient'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'db_index': 'True'}),
'type_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'})
},
u'zerver.referral': {
'Meta': {'object_name': 'Referral'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.stream': {
'Meta': {'unique_together': "(('name', 'realm'),)", 'object_name': 'Stream'},
'date_created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email_token': ('django.db.models.fields.CharField', [], {'default': "'cb2e1f76841fb35fed5e8f46315a3255'", 'max_length': '32'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invite_only': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '60', 'db_index': 'True'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"})
},
u'zerver.streamcolor': {
'Meta': {'object_name': 'StreamColor'},
'color': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'subscription': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Subscription']"})
},
u'zerver.subscription': {
'Meta': {'unique_together': "(('user_profile', 'recipient'),)", 'object_name': 'Subscription'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'color': ('django.db.models.fields.CharField', [], {'default': "'#c2c2c2'", 'max_length': '10'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'in_home_view': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}),
'notifications': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Recipient']"}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.useractivity': {
'Meta': {'unique_together': "(('user_profile', 'client', 'query'),)", 'object_name': 'UserActivity'},
'client': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Client']"}),
'count': ('django.db.models.fields.IntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_visit': ('django.db.models.fields.DateTimeField', [], {}),
'query': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.useractivityinterval': {
'Meta': {'object_name': 'UserActivityInterval'},
'end': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'start': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.usermessage': {
'Meta': {'unique_together': "(('user_profile', 'message'),)", 'object_name': 'UserMessage'},
'flags': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Message']"}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.userpresence': {
'Meta': {'unique_together': "(('user_profile', 'client'),)", 'object_name': 'UserPresence'},
'client': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Client']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'status': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {}),
'user_profile': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']"})
},
u'zerver.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'alert_words': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'api_key': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'avatar_source': ('django.db.models.fields.CharField', [], {'default': "'G'", 'max_length': '1'}),
'bot_owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.UserProfile']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'db_index': 'True'}),
'enable_desktop_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_offline_email_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_offline_push_notifications': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enable_sounds': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'enter_sends': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
'full_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'invites_granted': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'invites_used': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_bot': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_pointer_updater': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'last_reminder': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True'}),
'muted_topics': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'onboarding_steps': ('django.db.models.fields.TextField', [], {'default': "'[]'"}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'pointer': ('django.db.models.fields.IntegerField', [], {}),
'rate_limits': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
'realm': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['zerver.Realm']"}),
'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'tutorial_status': ('django.db.models.fields.CharField', [], {'default': "'W'", 'max_length': '1'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"})
}
}
complete_apps = ['zerver']

View File

@@ -128,6 +128,10 @@ class Realm(models.Model):
('administer', "Administer a realm"),
)
class RealmAlias(models.Model):
realm = models.ForeignKey(Realm, null=True)
domain = models.CharField(max_length=80, db_index=True, unique=True)
# These functions should only be used on email addresses that have
# been validated via django.core.validators.validate_email
#

View File

@@ -39,7 +39,7 @@ from zerver.lib.actions import bulk_remove_subscriptions, \
user_email_is_unique, do_invite_users, do_refer_friend, compute_mit_user_fullname, \
do_add_alert_words, do_remove_alert_words, do_set_alert_words, get_subscriber_emails, \
do_set_muted_topics, do_rename_stream, \
notify_for_streams_by_default, do_change_enable_offline_push_notifications
notify_for_streams_by_default, do_change_enable_offline_push_notifications, alias_for_realm
from zerver.lib.create_user import random_api_key
from zerver.lib.push_notifications import num_push_devices_for_user
from zerver.forms import RegistrationForm, HomepageForm, ToSForm, CreateBotForm, \
@@ -554,6 +554,10 @@ def create_preregistration_user(email, request):
if email_to_domain(email) == "mit.edu" and not domain:
prereg_user, created = MitUser.objects.get_or_create(email=email)
else:
realm = alias_for_realm(email_to_domain(email))
if realm is not None:
domain = realm.domain
prereg_user = PreregistrationUser(email=email, realm=get_realm(domain))
prereg_user.save()