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.
This commit is contained in:
Greg Price 2017-12-06 21:24:40 -08:00
parent 69a7069ac4
commit dc1eeef30a
4 changed files with 48 additions and 2 deletions

View File

@ -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',
),
]

View File

@ -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

View File

@ -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

View File

@ -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,