mirror of https://github.com/zulip/zulip.git
signup: Implement use of MultiUseInvite.status attribute.
This allows us to revoke MultiUseInvites by changing their .status instead of deleting them (which has been deleting the helpful tracking information on PreregistrationUsers about which MultiUseInvite they came from).
This commit is contained in:
parent
977a043d03
commit
d201229df8
|
@ -99,7 +99,7 @@ def get_object_from_key(
|
||||||
raise ConfirmationKeyException(ConfirmationKeyException.EXPIRED)
|
raise ConfirmationKeyException(ConfirmationKeyException.EXPIRED)
|
||||||
|
|
||||||
if mark_object_used:
|
if mark_object_used:
|
||||||
# MultiuseInvite objects have no status field, since they are
|
# MultiuseInvite objects do not use the STATUS_USED status, since they are
|
||||||
# intended to be used more than once.
|
# intended to be used more than once.
|
||||||
assert confirmation.type != Confirmation.MULTIUSE_INVITE
|
assert confirmation.type != Confirmation.MULTIUSE_INVITE
|
||||||
assert hasattr(obj, "status")
|
assert hasattr(obj, "status")
|
||||||
|
|
|
@ -10,6 +10,7 @@ from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from analytics.lib.counts import COUNT_STATS, do_increment_logging_stat
|
from analytics.lib.counts import COUNT_STATS, do_increment_logging_stat
|
||||||
from analytics.models import RealmCount
|
from analytics.models import RealmCount
|
||||||
|
from confirmation import settings as confirmation_settings
|
||||||
from confirmation.models import Confirmation, confirmation_url, create_confirmation_link
|
from confirmation.models import Confirmation, confirmation_url, create_confirmation_link
|
||||||
from zerver.lib.email_validation import (
|
from zerver.lib.email_validation import (
|
||||||
get_existing_user_errors,
|
get_existing_user_errors,
|
||||||
|
@ -392,7 +393,8 @@ def do_revoke_multi_use_invite(multiuse_invite: MultiuseInvite) -> None:
|
||||||
Confirmation.objects.filter(
|
Confirmation.objects.filter(
|
||||||
content_type=content_type, object_id=multiuse_invite.id
|
content_type=content_type, object_id=multiuse_invite.id
|
||||||
).delete()
|
).delete()
|
||||||
multiuse_invite.delete()
|
multiuse_invite.status = confirmation_settings.STATUS_REVOKED
|
||||||
|
multiuse_invite.save(update_fields=["status"])
|
||||||
notify_invites_changed(realm)
|
notify_invites_changed(realm)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.0.7 on 2022-09-11 22:03
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0421_migrate_pronouns_custom_profile_fields"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="multiuseinvite",
|
||||||
|
name="status",
|
||||||
|
field=models.IntegerField(default=0),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2333,6 +2333,12 @@ class MultiuseInvite(models.Model):
|
||||||
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||||
invited_as = models.PositiveSmallIntegerField(default=PreregistrationUser.INVITE_AS["MEMBER"])
|
invited_as = models.PositiveSmallIntegerField(default=PreregistrationUser.INVITE_AS["MEMBER"])
|
||||||
|
|
||||||
|
# status for tracking whether the invite has been revoked.
|
||||||
|
# If revoked, set to confirmation.settings.STATUS_REVOKED.
|
||||||
|
# STATUS_USED is not supported, because these objects are supposed
|
||||||
|
# to be usable multiple times.
|
||||||
|
status = models.IntegerField(default=0)
|
||||||
|
|
||||||
|
|
||||||
class EmailChangeStatus(models.Model):
|
class EmailChangeStatus(models.Model):
|
||||||
new_email = models.EmailField()
|
new_email = models.EmailField()
|
||||||
|
|
|
@ -40,6 +40,7 @@ from zerver.actions.invites import (
|
||||||
do_create_multiuse_invite_link,
|
do_create_multiuse_invite_link,
|
||||||
do_get_invites_controlled_by_user,
|
do_get_invites_controlled_by_user,
|
||||||
do_invite_users,
|
do_invite_users,
|
||||||
|
do_revoke_multi_use_invite,
|
||||||
)
|
)
|
||||||
from zerver.actions.realm_settings import (
|
from zerver.actions.realm_settings import (
|
||||||
do_deactivate_realm,
|
do_deactivate_realm,
|
||||||
|
@ -2710,10 +2711,13 @@ class InvitationsTestCase(InviteUserBase):
|
||||||
)
|
)
|
||||||
result = self.client_delete("/json/invites/multiuse/" + str(multiuse_invite.id))
|
result = self.client_delete("/json/invites/multiuse/" + str(multiuse_invite.id))
|
||||||
self.assertEqual(result.status_code, 200)
|
self.assertEqual(result.status_code, 200)
|
||||||
self.assertIsNone(MultiuseInvite.objects.filter(id=multiuse_invite.id).first())
|
self.assertEqual(
|
||||||
|
MultiuseInvite.objects.get(id=multiuse_invite.id).status,
|
||||||
|
confirmation_settings.STATUS_REVOKED,
|
||||||
|
)
|
||||||
# Test that trying to double-delete fails
|
# Test that trying to double-delete fails
|
||||||
error_result = self.client_delete("/json/invites/multiuse/" + str(multiuse_invite.id))
|
error_result = self.client_delete("/json/invites/multiuse/" + str(multiuse_invite.id))
|
||||||
self.assert_json_error(error_result, "No such invitation")
|
self.assert_json_error(error_result, "Invitation has already been revoked")
|
||||||
|
|
||||||
# Test deleting owner mutiuse_invite.
|
# Test deleting owner mutiuse_invite.
|
||||||
multiuse_invite = MultiuseInvite.objects.create(
|
multiuse_invite = MultiuseInvite.objects.create(
|
||||||
|
@ -2731,7 +2735,10 @@ class InvitationsTestCase(InviteUserBase):
|
||||||
self.login("desdemona")
|
self.login("desdemona")
|
||||||
result = self.client_delete("/json/invites/multiuse/" + str(multiuse_invite.id))
|
result = self.client_delete("/json/invites/multiuse/" + str(multiuse_invite.id))
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
self.assertIsNone(MultiuseInvite.objects.filter(id=multiuse_invite.id).first())
|
self.assertEqual(
|
||||||
|
MultiuseInvite.objects.get(id=multiuse_invite.id).status,
|
||||||
|
confirmation_settings.STATUS_REVOKED,
|
||||||
|
)
|
||||||
|
|
||||||
# Test deleting multiuse invite from another realm
|
# Test deleting multiuse invite from another realm
|
||||||
mit_realm = get_realm("zephyr")
|
mit_realm = get_realm("zephyr")
|
||||||
|
@ -2749,6 +2756,10 @@ class InvitationsTestCase(InviteUserBase):
|
||||||
)
|
)
|
||||||
self.assert_json_error(error_result, "No such invitation")
|
self.assert_json_error(error_result, "No such invitation")
|
||||||
|
|
||||||
|
non_existent_id = MultiuseInvite.objects.count() + 9999
|
||||||
|
error_result = self.client_delete(f"/json/invites/multiuse/{non_existent_id}")
|
||||||
|
self.assert_json_error(error_result, "No such invitation")
|
||||||
|
|
||||||
def test_successful_resend_invitation(self) -> None:
|
def test_successful_resend_invitation(self) -> None:
|
||||||
"""
|
"""
|
||||||
A POST call to /json/invites/<ID>/resend should send an invitation reminder email
|
A POST call to /json/invites/<ID>/resend should send an invitation reminder email
|
||||||
|
@ -3056,6 +3067,18 @@ class MultiuseInviteTest(ZulipTestCase):
|
||||||
self.assertEqual(result.status_code, 404)
|
self.assertEqual(result.status_code, 404)
|
||||||
self.assert_in_response("The confirmation link has expired or been deactivated.", result)
|
self.assert_in_response("The confirmation link has expired or been deactivated.", result)
|
||||||
|
|
||||||
|
def test_revoked_multiuse_link(self) -> None:
|
||||||
|
email = self.nonreg_email("newuser")
|
||||||
|
invite_link = self.generate_multiuse_invite_link()
|
||||||
|
multiuse_invite = MultiuseInvite.objects.last()
|
||||||
|
assert multiuse_invite is not None
|
||||||
|
do_revoke_multi_use_invite(multiuse_invite)
|
||||||
|
|
||||||
|
result = self.client_post(invite_link, {"email": email})
|
||||||
|
|
||||||
|
self.assertEqual(result.status_code, 404)
|
||||||
|
self.assert_in_response("We couldn't find your confirmation link in the system.", result)
|
||||||
|
|
||||||
def test_invalid_multiuse_link(self) -> None:
|
def test_invalid_multiuse_link(self) -> None:
|
||||||
email = self.nonreg_email("newuser")
|
email = self.nonreg_email("newuser")
|
||||||
invite_link = "/join/invalid_key/"
|
invite_link = "/join/invalid_key/"
|
||||||
|
|
|
@ -5,6 +5,7 @@ from django.conf import settings
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from confirmation import settings as confirmation_settings
|
||||||
from zerver.actions.invites import (
|
from zerver.actions.invites import (
|
||||||
do_create_multiuse_invite_link,
|
do_create_multiuse_invite_link,
|
||||||
do_get_invites_controlled_by_user,
|
do_get_invites_controlled_by_user,
|
||||||
|
@ -147,6 +148,9 @@ def revoke_multiuse_invite(
|
||||||
|
|
||||||
check_if_owner_required(invite.invited_as, user_profile)
|
check_if_owner_required(invite.invited_as, user_profile)
|
||||||
|
|
||||||
|
if invite.status == confirmation_settings.STATUS_REVOKED:
|
||||||
|
raise JsonableError(_("Invitation has already been revoked"))
|
||||||
|
|
||||||
do_revoke_multi_use_invite(invite)
|
do_revoke_multi_use_invite(invite)
|
||||||
return json_success(request)
|
return json_success(request)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue