mirror of https://github.com/zulip/zulip.git
realm_reactivation: Prevent realm reactivation link reuse.
This uses the approach analogical to EmailChangeStatus for email change confirmation links.
This commit is contained in:
parent
46c6f33b10
commit
cf74d7d140
|
@ -36,6 +36,7 @@ from zerver.models import (
|
|||
MultiuseInvite,
|
||||
PreregistrationUser,
|
||||
Realm,
|
||||
RealmReactivationStatus,
|
||||
UserProfile,
|
||||
get_org_type_display_name,
|
||||
get_realm,
|
||||
|
@ -313,8 +314,9 @@ def support(
|
|||
]
|
||||
confirmations += get_confirmations([Confirmation.MULTIUSE_INVITE], multiuse_invite_ids)
|
||||
|
||||
realm_reactivation_status_objects = RealmReactivationStatus.objects.filter(realm__in=realms)
|
||||
confirmations += get_confirmations(
|
||||
[Confirmation.REALM_REACTIVATION], [realm.id for realm in realms]
|
||||
[Confirmation.REALM_REACTIVATION], [obj.id for obj in realm_reactivation_status_objects]
|
||||
)
|
||||
|
||||
context["confirmations"] = confirmations
|
||||
|
|
|
@ -21,6 +21,7 @@ from zerver.models import (
|
|||
Attachment,
|
||||
Realm,
|
||||
RealmAuditLog,
|
||||
RealmReactivationStatus,
|
||||
RealmUserDefault,
|
||||
ScheduledEmail,
|
||||
Stream,
|
||||
|
@ -460,7 +461,9 @@ def do_change_realm_plan_type(
|
|||
|
||||
|
||||
def do_send_realm_reactivation_email(realm: Realm, *, acting_user: Optional[UserProfile]) -> None:
|
||||
url = create_confirmation_link(realm, Confirmation.REALM_REACTIVATION)
|
||||
obj = RealmReactivationStatus.objects.create(realm=realm)
|
||||
|
||||
url = create_confirmation_link(obj, Confirmation.REALM_REACTIVATION)
|
||||
RealmAuditLog.objects.create(
|
||||
realm=realm,
|
||||
acting_user=acting_user,
|
||||
|
|
|
@ -145,6 +145,7 @@ ALL_ZULIP_TABLES = {
|
|||
"zerver_realmemoji",
|
||||
"zerver_realmfilter",
|
||||
"zerver_realmplayground",
|
||||
"zerver_realmreactivationstatus",
|
||||
"zerver_realmuserdefault",
|
||||
"zerver_recipient",
|
||||
"zerver_scheduledemail",
|
||||
|
@ -184,6 +185,7 @@ NON_EXPORTED_TABLES = {
|
|||
"zerver_multiuseinvite_streams",
|
||||
"zerver_preregistrationuser",
|
||||
"zerver_preregistrationuser_streams",
|
||||
"zerver_realmreactivationstatus",
|
||||
# Missed message addresses are low value to export since
|
||||
# missed-message email addresses include the server's hostname and
|
||||
# expire after a few days.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.0.6 on 2022-07-25 20:31
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("zerver", "0399_preregistrationuser_multiuse_invite"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="RealmReactivationStatus",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
||||
),
|
||||
),
|
||||
("status", models.IntegerField(default=0)),
|
||||
(
|
||||
"realm",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="zerver.realm"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -2315,6 +2315,15 @@ class EmailChangeStatus(models.Model):
|
|||
realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||
|
||||
|
||||
class RealmReactivationStatus(models.Model):
|
||||
id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID")
|
||||
# status: whether an object has been confirmed.
|
||||
# if confirmed, set to confirmation.settings.STATUS_USED
|
||||
status: int = models.IntegerField(default=0)
|
||||
|
||||
realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||
|
||||
|
||||
class AbstractPushDeviceToken(models.Model):
|
||||
APNS = 1
|
||||
GCM = 2
|
||||
|
|
|
@ -31,6 +31,7 @@ from zerver.models import (
|
|||
Message,
|
||||
Realm,
|
||||
RealmAuditLog,
|
||||
RealmReactivationStatus,
|
||||
RealmUserDefault,
|
||||
ScheduledEmail,
|
||||
Stream,
|
||||
|
@ -354,7 +355,9 @@ class RealmTest(ZulipTestCase):
|
|||
realm = get_realm("zulip")
|
||||
do_deactivate_realm(realm, acting_user=None)
|
||||
self.assertTrue(realm.deactivated)
|
||||
confirmation_url = create_confirmation_link(realm, Confirmation.REALM_REACTIVATION)
|
||||
|
||||
obj = RealmReactivationStatus.objects.create(realm=realm)
|
||||
confirmation_url = create_confirmation_link(obj, Confirmation.REALM_REACTIVATION)
|
||||
response = self.client_get(confirmation_url)
|
||||
self.assert_in_success_response(
|
||||
["Your organization has been successfully reactivated"], response
|
||||
|
@ -362,6 +365,11 @@ class RealmTest(ZulipTestCase):
|
|||
realm = get_realm("zulip")
|
||||
self.assertFalse(realm.deactivated)
|
||||
|
||||
# Make sure the link can't be reused.
|
||||
do_deactivate_realm(realm, acting_user=None)
|
||||
response = self.client_get(confirmation_url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_realm_reactivation_confirmation_object(self) -> None:
|
||||
realm = get_realm("zulip")
|
||||
do_deactivate_realm(realm, acting_user=None)
|
||||
|
|
|
@ -38,7 +38,7 @@ from zerver.lib.validator import (
|
|||
check_string_or_int,
|
||||
to_non_negative_int,
|
||||
)
|
||||
from zerver.models import Realm, RealmUserDefault, UserProfile
|
||||
from zerver.models import Realm, RealmReactivationStatus, RealmUserDefault, UserProfile
|
||||
from zerver.views.user_settings import check_settings_values
|
||||
|
||||
ORG_TYPE_IDS: List[int] = [t["id"] for t in Realm.ORG_TYPES.values()]
|
||||
|
@ -327,14 +327,16 @@ def check_subdomain_available(request: HttpRequest, subdomain: str) -> HttpRespo
|
|||
|
||||
def realm_reactivation(request: HttpRequest, confirmation_key: str) -> HttpResponse:
|
||||
try:
|
||||
realm = get_object_from_key(
|
||||
confirmation_key, [Confirmation.REALM_REACTIVATION], mark_object_used=False
|
||||
obj = get_object_from_key(
|
||||
confirmation_key, [Confirmation.REALM_REACTIVATION], mark_object_used=True
|
||||
)
|
||||
except ConfirmationKeyException:
|
||||
return render(request, "zerver/realm_reactivation_link_error.html", status=404)
|
||||
assert isinstance(realm, Realm)
|
||||
|
||||
assert isinstance(obj, RealmReactivationStatus)
|
||||
realm = obj.realm
|
||||
|
||||
do_reactivate_realm(realm)
|
||||
# TODO: After reactivating the realm, the confirmation link needs to be revoked in some way.
|
||||
|
||||
context = {"realm": realm}
|
||||
return render(request, "zerver/realm_reactivation.html", context)
|
||||
|
|
Loading…
Reference in New Issue