confirmation: Tighten logic around the mark_object_used parameter.

This commit is contained in:
Tim Abbott 2022-07-19 12:13:32 -07:00
parent 495671cca0
commit dcc03a453a
5 changed files with 27 additions and 5 deletions

View File

@ -52,6 +52,15 @@ ConfirmationObjT = Union[MultiuseInvite, PreregistrationUser, EmailChangeStatus,
def get_object_from_key( def get_object_from_key(
confirmation_key: str, confirmation_types: List[int], mark_object_used: bool = True confirmation_key: str, confirmation_types: List[int], mark_object_used: bool = True
) -> ConfirmationObjT: ) -> 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 # Confirmation keys used to be 40 characters
if len(confirmation_key) not in (24, 40): if len(confirmation_key) not in (24, 40):
raise ConfirmationKeyException(ConfirmationKeyException.WRONG_LENGTH) raise ConfirmationKeyException(ConfirmationKeyException.WRONG_LENGTH)
@ -67,7 +76,12 @@ def get_object_from_key(
obj = confirmation.content_object obj = confirmation.content_object
assert obj is not None 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.status = getattr(settings, "STATUS_USED", 1)
obj.save(update_fields=["status"]) obj.save(update_fields=["status"])
return obj return obj

View File

@ -191,7 +191,7 @@ def maybe_send_to_registration(
from_multiuse_invite = True from_multiuse_invite = True
try: try:
confirmation_obj = get_object_from_key( 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: except ConfirmationKeyException as exception:
return render_confirmation_key_error(request, exception) return render_confirmation_key_error(request, exception)

View File

@ -327,11 +327,15 @@ def check_subdomain_available(request: HttpRequest, subdomain: str) -> HttpRespo
def realm_reactivation(request: HttpRequest, confirmation_key: str) -> HttpResponse: def realm_reactivation(request: HttpRequest, confirmation_key: str) -> HttpResponse:
try: 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: except ConfirmationKeyException:
return render(request, "zerver/realm_reactivation_link_error.html") return render(request, "zerver/realm_reactivation_link_error.html")
assert isinstance(realm, Realm) assert isinstance(realm, Realm)
do_reactivate_realm(realm) do_reactivate_realm(realm)
# TODO: After reactivating the realm, the confirmation link needs to be revoked in some way.
context = {"realm": realm} context = {"realm": realm}
return render(request, "zerver/realm_reactivation.html", context) return render(request, "zerver/realm_reactivation.html", context)

View File

@ -751,7 +751,9 @@ def accounts_home_from_multiuse_invite(request: HttpRequest, confirmation_key: s
realm = get_realm_from_request(request) realm = get_realm_from_request(request)
multiuse_object: Optional[MultiuseInvite] = None multiuse_object: Optional[MultiuseInvite] = None
try: 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) assert isinstance(confirmation_obj, MultiuseInvite)
multiuse_object = confirmation_obj multiuse_object = confirmation_obj
if realm != multiuse_object.realm: if realm != multiuse_object.realm:

View File

@ -18,7 +18,9 @@ def process_unsubscribe(
unsubscribe_function: Callable[[UserProfile], None], unsubscribe_function: Callable[[UserProfile], None],
) -> HttpResponse: ) -> HttpResponse:
try: 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: except ConfirmationKeyException:
return render(request, "zerver/unsubscribe_link_error.html") return render(request, "zerver/unsubscribe_link_error.html")