From dc1eeef30ad2172ecc346c5a72446e6cb5dd7883 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 6 Dec 2017 21:24:40 -0800 Subject: [PATCH] antispam: Make a setting for default Realm.max_invites. This makes this value much easier for a server admin to change than it was when embedded directly in the code. (Note this entire mechanism already only applies on a server open for anyone to create a realm.) Doing this also means getting the default out of the database. Instead, we make the column nullable, and when it's NULL in the database, treat that as whatever the current default is. This better matches anyway the likely model where there are a few realms with specially-set values, and everything else should be treated uniformly. The migration contains a `RenameField` step, which sounds scary operationally -- but it really does mean just the *field*, in the model within the Python code. The underlying column's name doesn't change. --- .../0148_max_invites_forget_default.py | 25 +++++++++++++++++++ zerver/models.py | 13 ++++++++-- zerver/tests/test_signup.py | 9 +++++++ zproject/settings.py | 3 +++ 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 zerver/migrations/0148_max_invites_forget_default.py diff --git a/zerver/migrations/0148_max_invites_forget_default.py b/zerver/migrations/0148_max_invites_forget_default.py new file mode 100644 index 0000000000..297faf498a --- /dev/null +++ b/zerver/migrations/0148_max_invites_forget_default.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-02-10 02:59 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('zerver', '0147_realm_disallow_disposable_email_addresses'), + ] + + operations = [ + migrations.AlterField( + model_name='realm', + name='max_invites', + field=models.IntegerField(null=True, db_column='max_invites'), + ), + migrations.RenameField( + model_name='realm', + old_name='max_invites', + new_name='_max_invites', + ), + ] diff --git a/zerver/models.py b/zerver/models.py index f51e257cc9..1d8855cbd4 100644 --- a/zerver/models.py +++ b/zerver/models.py @@ -185,8 +185,7 @@ class Realm(models.Model): authentication_methods = BitField(flags=AUTHENTICATION_FLAGS, default=2**31 - 1) # type: BitHandler waiting_period_threshold = models.PositiveIntegerField(default=0) # type: int - DEFAULT_MAX_INVITES = 100 - max_invites = models.IntegerField(default=DEFAULT_MAX_INVITES) # type: int + _max_invites = models.IntegerField(null=True, db_column='max_invites') # type: int message_visibility_limit = models.IntegerField(null=True) # type: int # See upload_quota_bytes; don't interpret upload_quota_gb directly. upload_quota_gb = models.IntegerField(null=True) # type: Optional[int] @@ -285,6 +284,16 @@ class Realm(models.Model): return self.signup_notifications_stream return None + @property + def max_invites(self) -> int: + if self._max_invites is None: + return settings.INVITES_DEFAULT_REALM_DAILY_MAX + return self._max_invites + + @max_invites.setter + def max_invites(self, value: int) -> None: + self._max_invites = value + def upload_quota_bytes(self) -> Optional[int]: if self.upload_quota_gb is None: return None diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py index aa684cd075..5963782487 100644 --- a/zerver/tests/test_signup.py +++ b/zerver/tests/test_signup.py @@ -647,6 +647,15 @@ earl-test@zulip.com""", ["Denmark"])) self.check_sent_emails(["bob-test@zulip.com", "carol-test@zulip.com", "dave-test@zulip.com", "earl-test@zulip.com"]) + def test_max_invites_model(self) -> None: + realm = get_realm("zulip") + self.assertEqual(realm.max_invites, settings.INVITES_DEFAULT_REALM_DAILY_MAX) + realm.max_invites = 3 + realm.save() + self.assertEqual(get_realm("zulip").max_invites, 3) + realm.max_invites = settings.INVITES_DEFAULT_REALM_DAILY_MAX + realm.save() + def test_invite_too_many_users(self) -> None: # Only a light test of this pathway; e.g. doesn't test that # the limit gets reset after 24 hours diff --git a/zproject/settings.py b/zproject/settings.py index ef90645d95..1ec266c4f5 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -306,6 +306,9 @@ DEFAULT_SETTINGS.update({ # # A non-admin user who's joined an open realm this recently can't invite at all. 'INVITES_MIN_USER_AGE_DAYS': 3, + # Default for a realm's `max_invites`; which applies per day, + # and only applies if OPEN_REALM_CREATION is true. + 'INVITES_DEFAULT_REALM_DAILY_MAX': 100, # Controls for which links are published in portico footers/headers/etc. 'EMAIL_DELIVERER_DISABLED': False,