diff --git a/confirmation/models.py b/confirmation/models.py index f42f396c6a..ed22f97809 100644 --- a/confirmation/models.py +++ b/confirmation/models.py @@ -52,6 +52,15 @@ ConfirmationObjT = Union[MultiuseInvite, PreregistrationUser, EmailChangeStatus, def get_object_from_key( confirmation_key: str, confirmation_types: List[int], mark_object_used: bool = True ) -> ConfirmationObjT: + """Access a confirmation object from one of the provided confirmation + types with the provided key. + + The mark_object_used parameter determines whether to mark the + confirmation object as used (which generally prevents it from + being used again). It should always be False for MultiuseInvite + objects, since they are intended to be used multiple times. + """ + # Confirmation keys used to be 40 characters if len(confirmation_key) not in (24, 40): raise ConfirmationKeyException(ConfirmationKeyException.WRONG_LENGTH) @@ -67,7 +76,12 @@ def get_object_from_key( obj = confirmation.content_object assert obj is not None - if mark_object_used and hasattr(obj, "status"): + + if mark_object_used: + # MultiuseInvite objects have no status field, since they are + # intended to be used more than once. + assert confirmation.type != Confirmation.MULTIUSE_INVITE + assert hasattr(obj, "status") obj.status = getattr(settings, "STATUS_USED", 1) obj.save(update_fields=["status"]) return obj diff --git a/zerver/views/auth.py b/zerver/views/auth.py index 0afa60e775..c1011781c6 100644 --- a/zerver/views/auth.py +++ b/zerver/views/auth.py @@ -191,7 +191,7 @@ def maybe_send_to_registration( from_multiuse_invite = True try: confirmation_obj = get_object_from_key( - multiuse_object_key, [Confirmation.MULTIUSE_INVITE] + multiuse_object_key, [Confirmation.MULTIUSE_INVITE], mark_object_used=False ) except ConfirmationKeyException as exception: return render_confirmation_key_error(request, exception) diff --git a/zerver/views/realm.py b/zerver/views/realm.py index acfee2e3e3..bbf06be44c 100644 --- a/zerver/views/realm.py +++ b/zerver/views/realm.py @@ -327,11 +327,15 @@ 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]) + realm = get_object_from_key( + confirmation_key, [Confirmation.REALM_REACTIVATION], mark_object_used=False + ) except ConfirmationKeyException: return render(request, "zerver/realm_reactivation_link_error.html") assert isinstance(realm, 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) diff --git a/zerver/views/registration.py b/zerver/views/registration.py index f577c85ea9..18bf9f9a0e 100644 --- a/zerver/views/registration.py +++ b/zerver/views/registration.py @@ -751,7 +751,9 @@ def accounts_home_from_multiuse_invite(request: HttpRequest, confirmation_key: s realm = get_realm_from_request(request) multiuse_object: Optional[MultiuseInvite] = None try: - confirmation_obj = get_object_from_key(confirmation_key, [Confirmation.MULTIUSE_INVITE]) + confirmation_obj = get_object_from_key( + confirmation_key, [Confirmation.MULTIUSE_INVITE], mark_object_used=False + ) assert isinstance(confirmation_obj, MultiuseInvite) multiuse_object = confirmation_obj if realm != multiuse_object.realm: diff --git a/zerver/views/unsubscribe.py b/zerver/views/unsubscribe.py index 3ede06d94c..095237073a 100644 --- a/zerver/views/unsubscribe.py +++ b/zerver/views/unsubscribe.py @@ -18,7 +18,9 @@ def process_unsubscribe( unsubscribe_function: Callable[[UserProfile], None], ) -> HttpResponse: try: - user_profile = get_object_from_key(confirmation_key, [Confirmation.UNSUBSCRIBE]) + user_profile = get_object_from_key( + confirmation_key, [Confirmation.UNSUBSCRIBE], mark_object_used=False + ) except ConfirmationKeyException: return render(request, "zerver/unsubscribe_link_error.html")