realm_reactivation: Use redirect-to-POST trick.

Uses the approach done for email change confirmations in #34980 to avoid
triggering a reactivation via just a GET request. Instead, the GET
should return a page which will trigger the browser to then POST the key
to the endpoint.
This commit is contained in:
Mateusz Mandera
2025-07-17 02:30:18 +08:00
committed by Tim Abbott
parent 5b00cb6753
commit 4210ccc5db
4 changed files with 38 additions and 6 deletions

View File

@@ -273,7 +273,7 @@ _properties = {
"join", validity_in_days=settings.INVITATION_LINK_VALIDITY_DAYS "join", validity_in_days=settings.INVITATION_LINK_VALIDITY_DAYS
), ),
Confirmation.REALM_CREATION: ConfirmationType("get_prereg_key_and_redirect"), Confirmation.REALM_CREATION: ConfirmationType("get_prereg_key_and_redirect"),
Confirmation.REALM_REACTIVATION: ConfirmationType("realm_reactivation"), Confirmation.REALM_REACTIVATION: ConfirmationType("realm_reactivation_get"),
} }
if settings.ZILENCER_ENABLED: if settings.ZILENCER_ENABLED:
_properties[Confirmation.REMOTE_SERVER_BILLING_LEGACY_LOGIN] = ConfirmationType( _properties[Confirmation.REMOTE_SERVER_BILLING_LEGACY_LOGIN] = ConfirmationType(

View File

@@ -514,7 +514,11 @@ class RealmTest(ZulipTestCase):
obj = RealmReactivationStatus.objects.create(realm=realm) obj = RealmReactivationStatus.objects.create(realm=realm)
confirmation_url = create_confirmation_link(obj, Confirmation.REALM_REACTIVATION) confirmation_url = create_confirmation_link(obj, Confirmation.REALM_REACTIVATION)
key = confirmation_url.split("/")[-1]
response = self.client_get(confirmation_url) response = self.client_get(confirmation_url)
self.assert_in_success_response(["redirect-to-post-form"], response)
response = self.client_post("/reactivate/", {"key": key})
self.assert_in_success_response( self.assert_in_success_response(
["Your organization has been successfully reactivated"], response ["Your organization has been successfully reactivated"], response
) )
@@ -527,6 +531,8 @@ class RealmTest(ZulipTestCase):
) )
response = self.client_get(confirmation_url) response = self.client_get(confirmation_url)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
response = self.client_post("/reactivate/", {"key": key})
self.assertEqual(response.status_code, 404)
def test_realm_reactivation_confirmation_object(self) -> None: def test_realm_reactivation_confirmation_object(self) -> None:
realm = get_realm("zulip") realm = get_realm("zulip")
@@ -650,7 +656,12 @@ class RealmTest(ZulipTestCase):
self.assertIn("Dear former administrators", mail.outbox[0].body) self.assertIn("Dear former administrators", mail.outbox[0].body)
admins = realm.get_human_admin_users() admins = realm.get_human_admin_users()
confirmation_url = self.get_confirmation_url_from_outbox(admins[0].delivery_email) confirmation_url = self.get_confirmation_url_from_outbox(admins[0].delivery_email)
key = confirmation_url.split("/")[-1]
response = self.client_get(confirmation_url) response = self.client_get(confirmation_url)
self.assert_in_success_response(["redirect-to-post-form"], response)
response = self.client_post("/reactivate/", {"key": key})
self.assert_in_success_response( self.assert_in_success_response(
["Your organization has been successfully reactivated"], response ["Your organization has been successfully reactivated"], response
) )

View File

@@ -6,6 +6,7 @@ from django.core.exceptions import ValidationError
from django.db import transaction from django.db import transaction
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.shortcuts import render from django.shortcuts import render
from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.decorators.http import require_safe from django.views.decorators.http import require_safe
from pydantic import Json, NonNegativeInt, StringConstraints from pydantic import Json, NonNegativeInt, StringConstraints
@@ -28,7 +29,7 @@ from zerver.actions.realm_settings import (
parse_and_set_setting_value_if_required, parse_and_set_setting_value_if_required,
validate_authentication_methods_dict_from_api, validate_authentication_methods_dict_from_api,
) )
from zerver.decorator import require_realm_admin, require_realm_owner from zerver.decorator import require_post, require_realm_admin, require_realm_owner
from zerver.forms import check_subdomain_available as check_subdomain from zerver.forms import check_subdomain_available as check_subdomain
from zerver.lib.demo_organizations import check_demo_organization_has_set_email from zerver.lib.demo_organizations import check_demo_organization_has_set_email
from zerver.lib.exceptions import JsonableError, OrganizationOwnerRequiredError from zerver.lib.exceptions import JsonableError, OrganizationOwnerRequiredError
@@ -576,11 +577,29 @@ def check_subdomain_available(request: HttpRequest, subdomain: str) -> HttpRespo
return json_success(request, data={"msg": e.message}) return json_success(request, data={"msg": e.message})
def realm_reactivation(request: HttpRequest, confirmation_key: str) -> HttpResponse: def realm_reactivation_get(request: HttpRequest, confirmation_key: str) -> HttpResponse:
try: try:
obj = get_object_from_key( get_object_from_key(
confirmation_key, [Confirmation.REALM_REACTIVATION], mark_object_used=True confirmation_key, [Confirmation.REALM_REACTIVATION], mark_object_used=False
) )
except ConfirmationKeyError: # nocoverage
return render(request, "zerver/realm_reactivation_link_error.html", status=404)
return render(
request,
"confirmation/redirect_to_post.html",
context={
"target_url": reverse("realm_reactivation"),
"key": confirmation_key,
},
)
@require_post
@typed_endpoint
def realm_reactivation(request: HttpRequest, *, key: str) -> HttpResponse:
try:
obj = get_object_from_key(key, [Confirmation.REALM_REACTIVATION], mark_object_used=True)
except ConfirmationKeyError: except ConfirmationKeyError:
return render(request, "zerver/realm_reactivation_link_error.html", status=404) return render(request, "zerver/realm_reactivation_link_error.html", status=404)

View File

@@ -124,6 +124,7 @@ from zerver.views.realm import (
check_subdomain_available, check_subdomain_available,
deactivate_realm, deactivate_realm,
realm_reactivation, realm_reactivation,
realm_reactivation_get,
update_realm, update_realm,
update_realm_user_settings_defaults, update_realm_user_settings_defaults,
) )
@@ -698,7 +699,8 @@ i18n_urls = [
path("new/", create_realm), path("new/", create_realm),
path("new/<creation_key>", create_realm, name="create_realm"), path("new/<creation_key>", create_realm, name="create_realm"),
# Realm reactivation # Realm reactivation
path("reactivate/<confirmation_key>", realm_reactivation, name="realm_reactivation"), path("reactivate/", realm_reactivation, name="realm_reactivation"),
path("reactivate/<confirmation_key>", realm_reactivation_get, name="realm_reactivation_get"),
# Login/registration # Login/registration
path("register/", accounts_home, name="register"), path("register/", accounts_home, name="register"),
path("login/", login_page, {"template_name": "zerver/login.html"}, name="login_page"), path("login/", login_page, {"template_name": "zerver/login.html"}, name="login_page"),