invites: Use expiration time in minutes instead of days.

This commit changes the invite API to accept invitation
expiration time in minutes since we are going to add a
custom option in further commits which would allow a user
to set expiration time in minutes, hours and weeks as well.
This commit is contained in:
Sahil Batra 2022-02-10 16:22:34 +05:30 committed by Tim Abbott
parent 6bb7f57ec2
commit 61365fbe21
19 changed files with 164 additions and 133 deletions

View File

@ -1371,12 +1371,12 @@ class TestLoggingCountStats(AnalyticsTestCase):
user = self.create_user(email="first@domain.tld") user = self.create_user(email="first@domain.tld")
stream, _ = self.create_stream_with_recipient() stream, _ = self.create_stream_with_recipient()
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
user, user,
["user1@domain.tld", "user2@domain.tld"], ["user1@domain.tld", "user2@domain.tld"],
[stream], [stream],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
assertInviteCountEquals(2) assertInviteCountEquals(2)
@ -1386,7 +1386,7 @@ class TestLoggingCountStats(AnalyticsTestCase):
user, user,
["user1@domain.tld", "user2@domain.tld"], ["user1@domain.tld", "user2@domain.tld"],
[stream], [stream],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
assertInviteCountEquals(4) assertInviteCountEquals(4)
@ -1396,7 +1396,7 @@ class TestLoggingCountStats(AnalyticsTestCase):
user, user,
["user3@domain.tld", "malformed"], ["user3@domain.tld", "malformed"],
[stream], [stream],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
except InvitationError: except InvitationError:
pass pass
@ -1408,7 +1408,7 @@ class TestLoggingCountStats(AnalyticsTestCase):
user, user,
["first@domain.tld", "user4@domain.tld"], ["first@domain.tld", "user4@domain.tld"],
[stream], [stream],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
except InvitationError: except InvitationError:
pass pass

View File

@ -258,7 +258,7 @@ class TestSupportEndpoint(ZulipTestCase):
check_preregistration_user_query_result(result, self.nonreg_email("test")) check_preregistration_user_query_result(result, self.nonreg_email("test"))
check_zulip_realm_query_result(result) check_zulip_realm_query_result(result)
invite_expires_in_days = 10 invite_expires_in_minutes = 10 * 24 * 60
stream_ids = [self.get_stream_id("Denmark")] stream_ids = [self.get_stream_id("Denmark")]
invitee_emails = [self.nonreg_email("test1")] invitee_emails = [self.nonreg_email("test1")]
self.client_post( self.client_post(
@ -266,7 +266,7 @@ class TestSupportEndpoint(ZulipTestCase):
{ {
"invitee_emails": invitee_emails, "invitee_emails": invitee_emails,
"stream_ids": orjson.dumps(stream_ids).decode(), "stream_ids": orjson.dumps(stream_ids).decode(),
"invite_expires_in_days": invite_expires_in_days, "invite_expires_in_minutes": invite_expires_in_minutes,
"invite_as": PreregistrationUser.INVITE_AS["MEMBER"], "invite_as": PreregistrationUser.INVITE_AS["MEMBER"],
}, },
) )
@ -282,7 +282,7 @@ class TestSupportEndpoint(ZulipTestCase):
do_create_multiuse_invite_link( do_create_multiuse_invite_link(
self.example_user("hamlet"), self.example_user("hamlet"),
invited_as=1, invited_as=1,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
result = self.client_get("/activity/support", {"q": "zulip"}) result = self.client_get("/activity/support", {"q": "zulip"})
check_multiuse_invite_link_query_result(result) check_multiuse_invite_link_query_result(result)

View File

@ -86,10 +86,10 @@ def create_confirmation_link(
obj: Union[Realm, HasRealmObject, OptionalHasRealmObject], obj: Union[Realm, HasRealmObject, OptionalHasRealmObject],
confirmation_type: int, confirmation_type: int,
*, *,
validity_in_days: Union[Optional[int], UnspecifiedValue] = UnspecifiedValue(), validity_in_minutes: Union[Optional[int], UnspecifiedValue] = UnspecifiedValue(),
url_args: Mapping[str, str] = {}, url_args: Mapping[str, str] = {},
) -> str: ) -> str:
# validity_in_days is an override for the default values which are # validity_in_minutes is an override for the default values which are
# determined by the confirmation_type - its main purpose is for use # determined by the confirmation_type - its main purpose is for use
# in tests which may want to have control over the exact expiration time. # in tests which may want to have control over the exact expiration time.
key = generate_key() key = generate_key()
@ -101,12 +101,12 @@ def create_confirmation_link(
current_time = timezone_now() current_time = timezone_now()
expiry_date = None expiry_date = None
if not isinstance(validity_in_days, UnspecifiedValue): if not isinstance(validity_in_minutes, UnspecifiedValue):
if validity_in_days is None: if validity_in_minutes is None:
expiry_date = None expiry_date = None
else: else:
assert validity_in_days is not None assert validity_in_minutes is not None
expiry_date = current_time + datetime.timedelta(days=validity_in_days) expiry_date = current_time + datetime.timedelta(minutes=validity_in_minutes)
else: else:
expiry_date = current_time + datetime.timedelta( expiry_date = current_time + datetime.timedelta(
days=_properties[confirmation_type].validity_in_days days=_properties[confirmation_type].validity_in_days

View File

@ -1,6 +1,6 @@
import autosize from "autosize"; import autosize from "autosize";
import ClipboardJS from "clipboard"; import ClipboardJS from "clipboard";
import {addDays} from "date-fns"; import {add} from "date-fns";
import $ from "jquery"; import $ from "jquery";
import copy_invite_link from "../templates/copy_invite_link.hbs"; import copy_invite_link from "../templates/copy_invite_link.hbs";
@ -52,7 +52,7 @@ function get_common_invitation_data() {
csrfmiddlewaretoken: $('input[name="csrfmiddlewaretoken"]').attr("value"), csrfmiddlewaretoken: $('input[name="csrfmiddlewaretoken"]').attr("value"),
invite_as, invite_as,
stream_ids: JSON.stringify(stream_ids), stream_ids: JSON.stringify(stream_ids),
invite_expires_in_days: expires_in, invite_expires_in_minutes: expires_in,
}; };
return data; return data;
} }
@ -208,7 +208,7 @@ function valid_to() {
if (!time_valid) { if (!time_valid) {
return $t({defaultMessage: "Never expires"}); return $t({defaultMessage: "Never expires"});
} }
const valid_to = addDays(new Date(), time_valid); const valid_to = add(new Date(), {minutes: time_valid});
return $t({defaultMessage: "Expires on {date}"}, {date: valid_to.toLocaleDateString()}); return $t({defaultMessage: "Expires on {date}"}, {date: valid_to.toLocaleDateString()});
} }

View File

@ -449,22 +449,22 @@ export const expires_in_values = {
// default: false, // default: false,
// }, // },
day: { day: {
value: 1, value: 24 * 60,
description: $t({defaultMessage: "1 day"}), description: $t({defaultMessage: "1 day"}),
default: false, default: false,
}, },
threeDays: { threeDays: {
value: 3, value: 3 * 24 * 60,
description: $t({defaultMessage: "3 days"}), description: $t({defaultMessage: "3 days"}),
default: false, default: false,
}, },
tenDays: { tenDays: {
value: 10, value: 10 * 24 * 60,
description: $t({defaultMessage: "10 days"}), description: $t({defaultMessage: "10 days"}),
default: true, default: true,
}, },
thirtyDays: { thirtyDays: {
value: 30, value: 30 * 24 * 60,
description: $t({defaultMessage: "30 days"}), description: $t({defaultMessage: "30 days"}),
default: false, default: false,
}, },

View File

@ -20,6 +20,11 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 6.0 ## Changes in Zulip 6.0
**Feature level 126**
* `POST /invites`, `POST /invites/multiuse`: Replaced `invite_expires_in_days`
parameter with `invite_expires_in_minutes`.
**Feature level 125** **Feature level 125**
* [`POST /register`](/api/register-queue), [`PATCH * [`POST /register`](/api/register-queue), [`PATCH

View File

@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3"
# Changes should be accompanied by documentation explaining what the # Changes should be accompanied by documentation explaining what the
# new level means in templates/zerver/api/changelog.md, as well as # new level means in templates/zerver/api/changelog.md, as well as
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`. # "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 125 API_FEATURE_LEVEL = 126
# Bump the minor PROVISION_VERSION to indicate that folks should provision # Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump # only when going from an old version of the code to a newer version. Bump

View File

@ -41,13 +41,13 @@ def do_send_confirmation_email(
invitee: PreregistrationUser, invitee: PreregistrationUser,
referrer: UserProfile, referrer: UserProfile,
email_language: str, email_language: str,
invite_expires_in_days: Union[Optional[int], UnspecifiedValue] = UnspecifiedValue(), invite_expires_in_minutes: Union[Optional[int], UnspecifiedValue] = UnspecifiedValue(),
) -> str: ) -> str:
""" """
Send the confirmation/welcome e-mail to an invited user. Send the confirmation/welcome e-mail to an invited user.
""" """
activation_url = create_confirmation_link( activation_url = create_confirmation_link(
invitee, Confirmation.INVITATION, validity_in_days=invite_expires_in_days invitee, Confirmation.INVITATION, validity_in_minutes=invite_expires_in_minutes
) )
context = { context = {
"referrer_full_name": referrer.full_name, "referrer_full_name": referrer.full_name,
@ -129,7 +129,7 @@ def do_invite_users(
invitee_emails: Collection[str], invitee_emails: Collection[str],
streams: Collection[Stream], streams: Collection[Stream],
*, *,
invite_expires_in_days: Optional[int], invite_expires_in_minutes: Optional[int],
invite_as: int = PreregistrationUser.INVITE_AS["MEMBER"], invite_as: int = PreregistrationUser.INVITE_AS["MEMBER"],
) -> None: ) -> None:
num_invites = len(invitee_emails) num_invites = len(invitee_emails)
@ -224,7 +224,7 @@ def do_invite_users(
"prereg_id": prereg_user.id, "prereg_id": prereg_user.id,
"referrer_id": user_profile.id, "referrer_id": user_profile.id,
"email_language": user_profile.realm.default_language, "email_language": user_profile.realm.default_language,
"invite_expires_in_days": invite_expires_in_days, "invite_expires_in_minutes": invite_expires_in_minutes,
} }
queue_json_publish("invites", event) queue_json_publish("invites", event)
@ -343,7 +343,7 @@ def revoke_invites_generated_by_user(user_profile: UserProfile) -> None:
def do_create_multiuse_invite_link( def do_create_multiuse_invite_link(
referred_by: UserProfile, referred_by: UserProfile,
invited_as: int, invited_as: int,
invite_expires_in_days: Optional[int], invite_expires_in_minutes: Optional[int],
streams: Sequence[Stream] = [], streams: Sequence[Stream] = [],
) -> str: ) -> str:
realm = referred_by.realm realm = referred_by.realm
@ -354,7 +354,7 @@ def do_create_multiuse_invite_link(
invite.save() invite.save()
notify_invites_changed(referred_by.realm) notify_invites_changed(referred_by.realm)
return create_confirmation_link( return create_confirmation_link(
invite, Confirmation.MULTIUSE_INVITE, validity_in_days=invite_expires_in_days invite, Confirmation.MULTIUSE_INVITE, validity_in_minutes=invite_expires_in_minutes
) )
@ -395,11 +395,11 @@ def do_resend_user_invite_email(prereg_user: PreregistrationUser) -> int:
expiry_date = prereg_user.confirmation.get().expiry_date expiry_date = prereg_user.confirmation.get().expiry_date
if expiry_date is None: if expiry_date is None:
invite_expires_in_days = None invite_expires_in_minutes = None
else: else:
# The resent invitation is reset to expire as long after the # The resent invitation is reset to expire as long after the
# reminder is sent as it lasted originally. # reminder is sent as it lasted originally.
invite_expires_in_days = (expiry_date - prereg_user.invited_at).days invite_expires_in_minutes = (expiry_date - prereg_user.invited_at).total_seconds() / 60
prereg_user.confirmation.clear() prereg_user.confirmation.clear()
do_increment_logging_stat( do_increment_logging_stat(
@ -412,7 +412,7 @@ def do_resend_user_invite_email(prereg_user: PreregistrationUser) -> int:
"prereg_id": prereg_user.id, "prereg_id": prereg_user.id,
"referrer_id": prereg_user.referred_by.id, "referrer_id": prereg_user.referred_by.id,
"email_language": prereg_user.referred_by.realm.default_language, "email_language": prereg_user.referred_by.realm.default_language,
"invite_expires_in_days": invite_expires_in_days, "invite_expires_in_minutes": invite_expires_in_minutes,
} }
queue_json_publish("invites", event) queue_json_publish("invites", event)

View File

@ -2246,7 +2246,7 @@ class PreregistrationUser(models.Model):
def filter_to_valid_prereg_users( def filter_to_valid_prereg_users(
query: QuerySet, query: QuerySet,
invite_expires_in_days: Union[Optional[int], UnspecifiedValue] = UnspecifiedValue(), invite_expires_in_minutes: Union[Optional[int], UnspecifiedValue] = UnspecifiedValue(),
) -> QuerySet: ) -> QuerySet:
""" """
If invite_expires_in_days is specified, we return only those PreregistrationUser If invite_expires_in_days is specified, we return only those PreregistrationUser
@ -2256,15 +2256,15 @@ def filter_to_valid_prereg_users(
revoked_value = confirmation_settings.STATUS_REVOKED revoked_value = confirmation_settings.STATUS_REVOKED
query = query.exclude(status__in=[active_value, revoked_value]) query = query.exclude(status__in=[active_value, revoked_value])
if invite_expires_in_days is None: if invite_expires_in_minutes is None:
# Since invite_expires_in_days is None, we're invitation will never # Since invite_expires_in_minutes is None, we're invitation will never
# expire, we do not need to check anything else and can simply return # expire, we do not need to check anything else and can simply return
# after excluding objects with active and revoked status. # after excluding objects with active and revoked status.
return query return query
assert invite_expires_in_days is not None assert invite_expires_in_minutes is not None
if not isinstance(invite_expires_in_days, UnspecifiedValue): if not isinstance(invite_expires_in_minutes, UnspecifiedValue):
lowest_datetime = timezone_now() - datetime.timedelta(days=invite_expires_in_days) lowest_datetime = timezone_now() - datetime.timedelta(minutes=invite_expires_in_minutes)
return query.filter(invited_at__gte=lowest_datetime) return query.filter(invited_at__gte=lowest_datetime)
else: else:
return query.filter( return query.filter(

View File

@ -1505,7 +1505,7 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
realm = get_realm("zulip") realm = get_realm("zulip")
iago = self.example_user("iago") iago = self.example_user("iago")
do_invite_users(iago, [email], [], invite_expires_in_days=2) do_invite_users(iago, [email], [], invite_expires_in_minutes=2 * 24 * 60)
account_data_dict = self.get_account_data_dict(email=email, name=name) account_data_dict = self.get_account_data_dict(email=email, name=name)
result = self.social_auth_test( result = self.social_auth_test(
@ -1555,9 +1555,9 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
referrer = self.example_user("hamlet") referrer = self.example_user("hamlet")
multiuse_obj = MultiuseInvite.objects.create(realm=realm, referred_by=referrer) multiuse_obj = MultiuseInvite.objects.create(realm=realm, referred_by=referrer)
multiuse_obj.streams.set(streams) multiuse_obj.streams.set(streams)
validity_in_days = 2 validity_in_minutes = 2 * 24 * 60
create_confirmation_link( create_confirmation_link(
multiuse_obj, Confirmation.MULTIUSE_INVITE, validity_in_days=validity_in_days multiuse_obj, Confirmation.MULTIUSE_INVITE, validity_in_minutes=validity_in_minutes
) )
multiuse_confirmation = Confirmation.objects.all().last() multiuse_confirmation = Confirmation.objects.all().last()
assert multiuse_confirmation is not None assert multiuse_confirmation is not None
@ -1602,9 +1602,9 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
realm=lear_realm, referred_by=UserProfile.objects.filter(realm=lear_realm).first() realm=lear_realm, referred_by=UserProfile.objects.filter(realm=lear_realm).first()
) )
multiuse_obj.streams.set(streams) multiuse_obj.streams.set(streams)
validity_in_days = 2 validity_in_minutes = 2 * 24 * 60
create_confirmation_link( create_confirmation_link(
multiuse_obj, Confirmation.MULTIUSE_INVITE, validity_in_days=validity_in_days multiuse_obj, Confirmation.MULTIUSE_INVITE, validity_in_minutes=validity_in_minutes
) )
multiuse_confirmation = Confirmation.objects.all().last() multiuse_confirmation = Confirmation.objects.all().last()
assert multiuse_confirmation is not None assert multiuse_confirmation is not None
@ -1840,15 +1840,15 @@ class SocialAuthBase(DesktopFlowTestingLib, ZulipTestCase, ABC):
email = self.nonreg_email("alice") email = self.nonreg_email("alice")
name = "Alice Jones" name = "Alice Jones"
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
iago, iago,
[email], [email],
[], [],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"], invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"],
) )
now = timezone_now() + datetime.timedelta(days=invite_expires_in_days + 1) now = timezone_now() + datetime.timedelta(days=3)
subdomain = "zulip" subdomain = "zulip"
realm = get_realm("zulip") realm = get_realm("zulip")
@ -4320,9 +4320,9 @@ class GoogleAuthBackendTest(SocialAuthBase):
referrer = self.example_user("hamlet") referrer = self.example_user("hamlet")
multiuse_obj = MultiuseInvite.objects.create(realm=realm, referred_by=referrer) multiuse_obj = MultiuseInvite.objects.create(realm=realm, referred_by=referrer)
multiuse_obj.streams.set(streams) multiuse_obj.streams.set(streams)
validity_in_days = 2 validity_in_minutes = 2 * 24 * 60
create_confirmation_link( create_confirmation_link(
multiuse_obj, Confirmation.MULTIUSE_INVITE, validity_in_days=validity_in_days multiuse_obj, Confirmation.MULTIUSE_INVITE, validity_in_minutes=validity_in_minutes
) )
multiuse_confirmation = Confirmation.objects.all().last() multiuse_confirmation = Confirmation.objects.all().last()
assert multiuse_confirmation is not None assert multiuse_confirmation is not None

View File

@ -773,13 +773,13 @@ class NormalActionsTest(BaseAction):
for stream_name in ["Denmark", "Scotland"]: for stream_name in ["Denmark", "Scotland"]:
streams.append(get_stream(stream_name, self.user_profile.realm)) streams.append(get_stream(stream_name, self.user_profile.realm))
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
events = self.verify_action( events = self.verify_action(
lambda: do_invite_users( lambda: do_invite_users(
self.user_profile, self.user_profile,
["foo@zulip.com"], ["foo@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
), ),
state_change_expected=False, state_change_expected=False,
) )
@ -791,12 +791,12 @@ class NormalActionsTest(BaseAction):
for stream_name in ["Denmark", "Verona"]: for stream_name in ["Denmark", "Verona"]:
streams.append(get_stream(stream_name, self.user_profile.realm)) streams.append(get_stream(stream_name, self.user_profile.realm))
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
events = self.verify_action( events = self.verify_action(
lambda: do_create_multiuse_invite_link( lambda: do_create_multiuse_invite_link(
self.user_profile, self.user_profile,
PreregistrationUser.INVITE_AS["MEMBER"], PreregistrationUser.INVITE_AS["MEMBER"],
invite_expires_in_days, invite_expires_in_minutes,
streams, streams,
), ),
state_change_expected=False, state_change_expected=False,
@ -806,12 +806,12 @@ class NormalActionsTest(BaseAction):
def test_deactivate_user_invites_changed_event(self) -> None: def test_deactivate_user_invites_changed_event(self) -> None:
self.user_profile = self.example_user("iago") self.user_profile = self.example_user("iago")
user_profile = self.example_user("cordelia") user_profile = self.example_user("cordelia")
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
user_profile, user_profile,
["foo@zulip.com"], ["foo@zulip.com"],
[], [],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
events = self.verify_action( events = self.verify_action(
@ -827,12 +827,12 @@ class NormalActionsTest(BaseAction):
for stream_name in ["Denmark", "Verona"]: for stream_name in ["Denmark", "Verona"]:
streams.append(get_stream(stream_name, self.user_profile.realm)) streams.append(get_stream(stream_name, self.user_profile.realm))
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
self.user_profile, self.user_profile,
["foo@zulip.com"], ["foo@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
prereg_users = PreregistrationUser.objects.filter( prereg_users = PreregistrationUser.objects.filter(
referred_by__realm=self.user_profile.realm referred_by__realm=self.user_profile.realm
@ -849,11 +849,11 @@ class NormalActionsTest(BaseAction):
for stream_name in ["Denmark", "Verona"]: for stream_name in ["Denmark", "Verona"]:
streams.append(get_stream(stream_name, self.user_profile.realm)) streams.append(get_stream(stream_name, self.user_profile.realm))
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_create_multiuse_invite_link( do_create_multiuse_invite_link(
self.user_profile, self.user_profile,
PreregistrationUser.INVITE_AS["MEMBER"], PreregistrationUser.INVITE_AS["MEMBER"],
invite_expires_in_days, invite_expires_in_minutes,
streams, streams,
) )
@ -872,12 +872,12 @@ class NormalActionsTest(BaseAction):
for stream_name in ["Denmark", "Scotland"]: for stream_name in ["Denmark", "Scotland"]:
streams.append(get_stream(stream_name, self.user_profile.realm)) streams.append(get_stream(stream_name, self.user_profile.realm))
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
self.user_profile, self.user_profile,
["foo@zulip.com"], ["foo@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
prereg_user = PreregistrationUser.objects.get(email="foo@zulip.com") prereg_user = PreregistrationUser.objects.get(email="foo@zulip.com")

View File

@ -35,7 +35,7 @@ class EmailTranslationTestCase(ZulipTestCase):
realm.default_language = "de" realm.default_language = "de"
realm.save() realm.save()
stream = get_realm_stream("Denmark", realm.id) stream = get_realm_stream("Denmark", realm.id)
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
self.login_user(hamlet) self.login_user(hamlet)
# TODO: Uncomment and replace with translation once we have German translations for the strings # TODO: Uncomment and replace with translation once we have German translations for the strings
@ -59,7 +59,7 @@ class EmailTranslationTestCase(ZulipTestCase):
{ {
"invitee_emails": "new-email@zulip.com", "invitee_emails": "new-email@zulip.com",
"stream_ids": orjson.dumps([stream.id]).decode(), "stream_ids": orjson.dumps([stream.id]).decode(),
"invite_expires_in_days": invite_expires_in_days, "invite_expires_in_minutes": invite_expires_in_minutes,
}, },
) )

View File

@ -613,24 +613,24 @@ class WorkerTest(ZulipTestCase):
PreregistrationUser.objects.create( PreregistrationUser.objects.create(
email=self.nonreg_email("bob"), referred_by=inviter, realm=inviter.realm email=self.nonreg_email("bob"), referred_by=inviter, realm=inviter.realm
) )
invite_expires_in_days = 4 invite_expires_in_minutes = 4 * 24 * 60
data: List[Dict[str, Any]] = [ data: List[Dict[str, Any]] = [
dict( dict(
prereg_id=prereg_alice.id, prereg_id=prereg_alice.id,
referrer_id=inviter.id, referrer_id=inviter.id,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
), ),
dict( dict(
prereg_id=prereg_alice.id, prereg_id=prereg_alice.id,
referrer_id=inviter.id, referrer_id=inviter.id,
email_language="en", email_language="en",
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
), ),
# Nonexistent prereg_id, as if the invitation was deleted # Nonexistent prereg_id, as if the invitation was deleted
dict( dict(
prereg_id=-1, prereg_id=-1,
referrer_id=inviter.id, referrer_id=inviter.id,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
), ),
] ]
for element in data: for element in data:

View File

@ -1050,7 +1050,7 @@ class InviteUserBase(ZulipTestCase):
self, self,
invitee_emails: str, invitee_emails: str,
stream_names: Sequence[str], stream_names: Sequence[str],
invite_expires_in_days: Optional[int] = settings.INVITATION_LINK_VALIDITY_DAYS, invite_expires_in_minutes: Optional[int] = settings.INVITATION_LINK_VALIDITY_MINUTES,
body: str = "", body: str = "",
invite_as: int = PreregistrationUser.INVITE_AS["MEMBER"], invite_as: int = PreregistrationUser.INVITE_AS["MEMBER"],
) -> HttpResponse: ) -> HttpResponse:
@ -1066,7 +1066,7 @@ class InviteUserBase(ZulipTestCase):
for stream_name in stream_names: for stream_name in stream_names:
stream_ids.append(self.get_stream_id(stream_name)) stream_ids.append(self.get_stream_id(stream_name))
invite_expires_in: Union[str, Optional[int]] = invite_expires_in_days invite_expires_in: Union[str, Optional[int]] = invite_expires_in_minutes
if invite_expires_in is None: if invite_expires_in is None:
invite_expires_in = orjson.dumps(None).decode() invite_expires_in = orjson.dumps(None).decode()
@ -1074,7 +1074,7 @@ class InviteUserBase(ZulipTestCase):
"/json/invites", "/json/invites",
{ {
"invitee_emails": invitee_emails, "invitee_emails": invitee_emails,
"invite_expires_in_days": invite_expires_in, "invite_expires_in_minutes": invite_expires_in,
"stream_ids": orjson.dumps(stream_ids).decode(), "stream_ids": orjson.dumps(stream_ids).decode(),
"invite_as": invite_as, "invite_as": invite_as,
}, },
@ -1981,9 +1981,9 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
data = {"email": invitee_email, "referrer_email": current_user.email} data = {"email": invitee_email, "referrer_email": current_user.email}
invitee = PreregistrationUser.objects.get(email=data["email"]) invitee = PreregistrationUser.objects.get(email=data["email"])
referrer = self.example_user(referrer_name) referrer = self.example_user(referrer_name)
validity_in_days = 2 validity_in_minutes = 2 * 24 * 60
link = create_confirmation_link( link = create_confirmation_link(
invitee, Confirmation.INVITATION, validity_in_days=validity_in_days invitee, Confirmation.INVITATION, validity_in_minutes=validity_in_minutes
) )
context = common_context(referrer) context = common_context(referrer)
context.update( context.update(
@ -2037,12 +2037,12 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
def test_no_invitation_reminder_when_link_expires_quickly(self) -> None: def test_no_invitation_reminder_when_link_expires_quickly(self) -> None:
self.login("hamlet") self.login("hamlet")
# Check invitation reminder email is scheduled with 4 day link expiry # Check invitation reminder email is scheduled with 4 day link expiry
self.invite("alice@zulip.com", ["Denmark"], invite_expires_in_days=4) self.invite("alice@zulip.com", ["Denmark"], invite_expires_in_minutes=4 * 24 * 60)
self.assertEqual( self.assertEqual(
ScheduledEmail.objects.filter(type=ScheduledEmail.INVITATION_REMINDER).count(), 1 ScheduledEmail.objects.filter(type=ScheduledEmail.INVITATION_REMINDER).count(), 1
) )
# Check invitation reminder email is not scheduled with 3 day link expiry # Check invitation reminder email is not scheduled with 3 day link expiry
self.invite("bob@zulip.com", ["Denmark"], invite_expires_in_days=3) self.invite("bob@zulip.com", ["Denmark"], invite_expires_in_minutes=3 * 24 * 60)
self.assertEqual( self.assertEqual(
ScheduledEmail.objects.filter(type=ScheduledEmail.INVITATION_REMINDER).count(), 1 ScheduledEmail.objects.filter(type=ScheduledEmail.INVITATION_REMINDER).count(), 1
) )
@ -2108,7 +2108,7 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
email=email, referred_by=inviter, realm=realm email=email, referred_by=inviter, realm=realm
) )
activation_url = create_confirmation_link( activation_url = create_confirmation_link(
prereg_user, Confirmation.INVITATION, validity_in_days=None prereg_user, Confirmation.INVITATION, validity_in_minutes=None
) )
confirmation = Confirmation.objects.last() confirmation = Confirmation.objects.last()
assert confirmation is not None assert confirmation is not None
@ -2126,32 +2126,32 @@ so we didn't send them an invitation. We did send invitations to everyone else!"
for stream_name in ["Denmark", "Scotland"]: for stream_name in ["Denmark", "Scotland"]:
streams.append(get_stream(stream_name, self.user_profile.realm)) streams.append(get_stream(stream_name, self.user_profile.realm))
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
self.user_profile, self.user_profile,
["foo@zulip.com"], ["foo@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
prereg_user = PreregistrationUser.objects.get(email="foo@zulip.com") prereg_user = PreregistrationUser.objects.get(email="foo@zulip.com")
do_invite_users( do_invite_users(
self.user_profile, self.user_profile,
["foo@zulip.com"], ["foo@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
do_invite_users( do_invite_users(
self.user_profile, self.user_profile,
["foo@zulip.com"], ["foo@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
# Also send an invite from a different realm. # Also send an invite from a different realm.
lear = get_realm("lear") lear = get_realm("lear")
lear_user = self.lear_user("cordelia") lear_user = self.lear_user("cordelia")
do_invite_users( do_invite_users(
lear_user, ["foo@zulip.com"], [], invite_expires_in_days=invite_expires_in_days lear_user, ["foo@zulip.com"], [], invite_expires_in_minutes=invite_expires_in_minutes
) )
invites = PreregistrationUser.objects.filter(email__iexact="foo@zulip.com") invites = PreregistrationUser.objects.filter(email__iexact="foo@zulip.com")
@ -2294,33 +2294,39 @@ class InvitationsTestCase(InviteUserBase):
for stream_name in ["Denmark", "Scotland"]: for stream_name in ["Denmark", "Scotland"]:
streams.append(get_stream(stream_name, user_profile.realm)) streams.append(get_stream(stream_name, user_profile.realm))
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
user_profile, user_profile,
["TestOne@zulip.com"], ["TestOne@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
do_invite_users( do_invite_users(
user_profile, user_profile,
["TestTwo@zulip.com"], ["TestTwo@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
do_invite_users( do_invite_users(
hamlet, ["TestThree@zulip.com"], streams, invite_expires_in_days=invite_expires_in_days hamlet,
["TestThree@zulip.com"],
streams,
invite_expires_in_minutes=invite_expires_in_minutes,
) )
do_invite_users( do_invite_users(
othello, ["TestFour@zulip.com"], streams, invite_expires_in_days=invite_expires_in_days othello,
["TestFour@zulip.com"],
streams,
invite_expires_in_minutes=invite_expires_in_minutes,
) )
do_invite_users( do_invite_users(
self.mit_user("sipbtest"), self.mit_user("sipbtest"),
["TestOne@mit.edu"], ["TestOne@mit.edu"],
[], [],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
do_create_multiuse_invite_link( do_create_multiuse_invite_link(
user_profile, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_days user_profile, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_minutes
) )
self.assert_length(do_get_invites_controlled_by_user(user_profile), 5) self.assert_length(do_get_invites_controlled_by_user(user_profile), 5)
self.assert_length(do_get_invites_controlled_by_user(hamlet), 1) self.assert_length(do_get_invites_controlled_by_user(hamlet), 1)
@ -2344,26 +2350,26 @@ class InvitationsTestCase(InviteUserBase):
for stream_name in ["Denmark", "Scotland"]: for stream_name in ["Denmark", "Scotland"]:
streams.append(get_stream(stream_name, user_profile.realm)) streams.append(get_stream(stream_name, user_profile.realm))
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
user_profile, user_profile,
["TestOne@zulip.com"], ["TestOne@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
with patch( with patch(
"confirmation.models.timezone_now", "confirmation.models.timezone_now",
return_value=timezone_now() - datetime.timedelta(days=invite_expires_in_days + 1), return_value=timezone_now() - datetime.timedelta(days=3),
): ):
do_invite_users( do_invite_users(
user_profile, user_profile,
["TestTwo@zulip.com"], ["TestTwo@zulip.com"],
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
do_create_multiuse_invite_link( do_create_multiuse_invite_link(
othello, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_days othello, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_minutes
) )
prereg_user_three = PreregistrationUser( prereg_user_three = PreregistrationUser(
@ -2371,11 +2377,13 @@ class InvitationsTestCase(InviteUserBase):
) )
prereg_user_three.save() prereg_user_three.save()
create_confirmation_link( create_confirmation_link(
prereg_user_three, Confirmation.INVITATION, validity_in_days=invite_expires_in_days prereg_user_three,
Confirmation.INVITATION,
validity_in_minutes=invite_expires_in_minutes,
) )
do_create_multiuse_invite_link( do_create_multiuse_invite_link(
hamlet, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_days hamlet, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_minutes
) )
result = self.client_get("/json/invites") result = self.client_get("/json/invites")
@ -2406,13 +2414,13 @@ class InvitationsTestCase(InviteUserBase):
user_profile, user_profile,
["TestOne@zulip.com"], ["TestOne@zulip.com"],
streams, streams,
invite_expires_in_days=None, invite_expires_in_minutes=None,
) )
do_invite_users( do_invite_users(
user_profile, user_profile,
["TestTwo@zulip.com"], ["TestTwo@zulip.com"],
streams, streams,
invite_expires_in_days=100, invite_expires_in_minutes=100 * 24 * 60,
) )
do_create_multiuse_invite_link( do_create_multiuse_invite_link(
user_profile, PreregistrationUser.INVITE_AS["MEMBER"], None user_profile, PreregistrationUser.INVITE_AS["MEMBER"], None
@ -2537,9 +2545,9 @@ class InvitationsTestCase(InviteUserBase):
multiuse_invite = MultiuseInvite.objects.create( multiuse_invite = MultiuseInvite.objects.create(
referred_by=self.example_user("hamlet"), realm=zulip_realm referred_by=self.example_user("hamlet"), realm=zulip_realm
) )
validity_in_days = 2 validity_in_minutes = 2 * 24 * 60
create_confirmation_link( create_confirmation_link(
multiuse_invite, Confirmation.MULTIUSE_INVITE, validity_in_days=validity_in_days multiuse_invite, Confirmation.MULTIUSE_INVITE, validity_in_minutes=validity_in_minutes
) )
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)
@ -2554,9 +2562,9 @@ class InvitationsTestCase(InviteUserBase):
realm=zulip_realm, realm=zulip_realm,
invited_as=PreregistrationUser.INVITE_AS["REALM_OWNER"], invited_as=PreregistrationUser.INVITE_AS["REALM_OWNER"],
) )
validity_in_days = 2 validity_in_minutes = 2
create_confirmation_link( create_confirmation_link(
multiuse_invite, Confirmation.MULTIUSE_INVITE, validity_in_days=validity_in_days multiuse_invite, Confirmation.MULTIUSE_INVITE, validity_in_minutes=validity_in_minutes
) )
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, "Must be an organization owner") self.assert_json_error(error_result, "Must be an organization owner")
@ -2571,9 +2579,11 @@ class InvitationsTestCase(InviteUserBase):
multiuse_invite_in_mit = MultiuseInvite.objects.create( multiuse_invite_in_mit = MultiuseInvite.objects.create(
referred_by=self.mit_user("sipbtest"), realm=mit_realm referred_by=self.mit_user("sipbtest"), realm=mit_realm
) )
validity_in_days = 2 validity_in_minutes = 2 * 24 * 60
create_confirmation_link( create_confirmation_link(
multiuse_invite_in_mit, Confirmation.MULTIUSE_INVITE, validity_in_days=validity_in_days multiuse_invite_in_mit,
Confirmation.MULTIUSE_INVITE,
validity_in_minutes=validity_in_minutes,
) )
error_result = self.client_delete( error_result = self.client_delete(
"/json/invites/multiuse/" + str(multiuse_invite_in_mit.id) "/json/invites/multiuse/" + str(multiuse_invite_in_mit.id)
@ -2830,10 +2840,10 @@ class MultiuseInviteTest(ZulipTestCase):
if date_sent is None: if date_sent is None:
date_sent = timezone_now() date_sent = timezone_now()
validity_in_days = 2 validity_in_minutes = 2 * 24 * 60
with patch("confirmation.models.timezone_now", return_value=date_sent): with patch("confirmation.models.timezone_now", return_value=date_sent):
return create_confirmation_link( return create_confirmation_link(
invite, Confirmation.MULTIUSE_INVITE, validity_in_days=validity_in_days invite, Confirmation.MULTIUSE_INVITE, validity_in_minutes=validity_in_minutes
) )
def check_user_able_to_register(self, email: str, invite_link: str) -> None: def check_user_able_to_register(self, email: str, invite_link: str) -> None:
@ -2861,8 +2871,7 @@ class MultiuseInviteTest(ZulipTestCase):
email2 = self.nonreg_email("test1") email2 = self.nonreg_email("test1")
email3 = self.nonreg_email("alice") email3 = self.nonreg_email("alice")
validity_in_days = 2 date_sent = timezone_now() - datetime.timedelta(days=1)
date_sent = timezone_now() - datetime.timedelta(days=validity_in_days - 1)
invite_link = self.generate_multiuse_invite_link(date_sent=date_sent) invite_link = self.generate_multiuse_invite_link(date_sent=date_sent)
self.check_user_able_to_register(email1, invite_link) self.check_user_able_to_register(email1, invite_link)
@ -2948,7 +2957,9 @@ class MultiuseInviteTest(ZulipTestCase):
def test_create_multiuse_link_api_call(self) -> None: def test_create_multiuse_link_api_call(self) -> None:
self.login("iago") self.login("iago")
result = self.client_post("/json/invites/multiuse", {"invite_expires_in_days": 2}) result = self.client_post(
"/json/invites/multiuse", {"invite_expires_in_minutes": 2 * 24 * 60}
)
self.assert_json_success(result) self.assert_json_success(result)
invite_link = result.json()["invite_link"] invite_link = result.json()["invite_link"]
@ -2962,7 +2973,10 @@ class MultiuseInviteTest(ZulipTestCase):
result = self.client_post( result = self.client_post(
"/json/invites/multiuse", "/json/invites/multiuse",
{"stream_ids": orjson.dumps(stream_ids).decode(), "invite_expires_in_days": 2}, {
"stream_ids": orjson.dumps(stream_ids).decode(),
"invite_expires_in_minutes": 2 * 24 * 60,
},
) )
self.assert_json_success(result) self.assert_json_success(result)
@ -2977,7 +2991,9 @@ class MultiuseInviteTest(ZulipTestCase):
self.realm.invite_to_realm_policy = Realm.POLICY_MEMBERS_ONLY self.realm.invite_to_realm_policy = Realm.POLICY_MEMBERS_ONLY
self.realm.save() self.realm.save()
result = self.client_post("/json/invites/multiuse", {"invite_expires_in_days": 2}) result = self.client_post(
"/json/invites/multiuse", {"invite_expires_in_minutes": 2 * 24 * 60}
)
self.assert_json_success(result) self.assert_json_success(result)
invite_link = result.json()["invite_link"] invite_link = result.json()["invite_link"]
@ -2993,7 +3009,7 @@ class MultiuseInviteTest(ZulipTestCase):
"/json/invites/multiuse", "/json/invites/multiuse",
{ {
"invite_as": orjson.dumps(PreregistrationUser.INVITE_AS["REALM_OWNER"]).decode(), "invite_as": orjson.dumps(PreregistrationUser.INVITE_AS["REALM_OWNER"]).decode(),
"invite_expires_in_days": 2, "invite_expires_in_minutes": 2 * 24 * 60,
}, },
) )
self.assert_json_error(result, "Must be an organization owner") self.assert_json_error(result, "Must be an organization owner")
@ -3003,7 +3019,7 @@ class MultiuseInviteTest(ZulipTestCase):
"/json/invites/multiuse", "/json/invites/multiuse",
{ {
"invite_as": orjson.dumps(PreregistrationUser.INVITE_AS["REALM_OWNER"]).decode(), "invite_as": orjson.dumps(PreregistrationUser.INVITE_AS["REALM_OWNER"]).decode(),
"invite_expires_in_days": 2, "invite_expires_in_minutes": 2 * 24 * 60,
}, },
) )
self.assert_json_success(result) self.assert_json_success(result)
@ -3015,7 +3031,10 @@ class MultiuseInviteTest(ZulipTestCase):
self.login("iago") self.login("iago")
result = self.client_post( result = self.client_post(
"/json/invites/multiuse", "/json/invites/multiuse",
{"stream_ids": orjson.dumps([54321]).decode(), "invite_expires_in_days": 2}, {
"stream_ids": orjson.dumps([54321]).decode(),
"invite_expires_in_minutes": 2 * 24 * 60,
},
) )
self.assert_json_error(result, "Invalid stream id 54321. No invites were sent.") self.assert_json_error(result, "Invalid stream id 54321. No invites were sent.")

View File

@ -795,12 +795,12 @@ class QueryCountTest(ZulipTestCase):
] ]
streams = [get_stream(stream_name, realm) for stream_name in stream_names] streams = [get_stream(stream_name, realm) for stream_name in stream_names]
invite_expires_in_days = 4 invite_expires_in_minutes = 4 * 24 * 60
do_invite_users( do_invite_users(
user_profile=self.example_user("hamlet"), user_profile=self.example_user("hamlet"),
invitee_emails=["fred@zulip.com"], invitee_emails=["fred@zulip.com"],
streams=streams, streams=streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
) )
prereg_user = PreregistrationUser.objects.get(email="fred@zulip.com") prereg_user = PreregistrationUser.objects.get(email="fred@zulip.com")
@ -1461,19 +1461,19 @@ class ActivateTest(ZulipTestCase):
iago = self.example_user("iago") iago = self.example_user("iago")
desdemona = self.example_user("desdemona") desdemona = self.example_user("desdemona")
invite_expires_in_days = 2 invite_expires_in_minutes = 2 * 24 * 60
do_invite_users( do_invite_users(
iago, iago,
["new1@zulip.com", "new2@zulip.com"], ["new1@zulip.com", "new2@zulip.com"],
[], [],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"], invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"],
) )
do_invite_users( do_invite_users(
desdemona, desdemona,
["new3@zulip.com", "new4@zulip.com"], ["new3@zulip.com", "new4@zulip.com"],
[], [],
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"], invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"],
) )
@ -1481,22 +1481,22 @@ class ActivateTest(ZulipTestCase):
iago, iago,
["new5@zulip.com"], ["new5@zulip.com"],
[], [],
invite_expires_in_days=None, invite_expires_in_minutes=None,
invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"], invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"],
) )
do_invite_users( do_invite_users(
desdemona, desdemona,
["new6@zulip.com"], ["new6@zulip.com"],
[], [],
invite_expires_in_days=None, invite_expires_in_minutes=None,
invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"], invite_as=PreregistrationUser.INVITE_AS["REALM_ADMIN"],
) )
iago_multiuse_key = do_create_multiuse_invite_link( iago_multiuse_key = do_create_multiuse_invite_link(
iago, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_days iago, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_minutes
).split("/")[-2] ).split("/")[-2]
desdemona_multiuse_key = do_create_multiuse_invite_link( desdemona_multiuse_key = do_create_multiuse_invite_link(
desdemona, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_days desdemona, PreregistrationUser.INVITE_AS["MEMBER"], invite_expires_in_minutes
).split("/")[-2] ).split("/")[-2]
iago_never_expire_multiuse_key = do_create_multiuse_invite_link( iago_never_expire_multiuse_key = do_create_multiuse_invite_link(

View File

@ -65,7 +65,7 @@ def generate_all_emails(request: HttpRequest) -> HttpResponse:
registered_email = "hamlet@zulip.com" registered_email = "hamlet@zulip.com"
unregistered_email_1 = "new-person@zulip.com" unregistered_email_1 = "new-person@zulip.com"
unregistered_email_2 = "new-person-2@zulip.com" unregistered_email_2 = "new-person-2@zulip.com"
invite_expires_in_days = settings.INVITATION_LINK_VALIDITY_DAYS invite_expires_in_minutes = settings.INVITATION_LINK_VALIDITY_MINUTES
realm = get_realm("zulip") realm = get_realm("zulip")
other_realm = Realm.objects.exclude(string_id="zulip").first() other_realm = Realm.objects.exclude(string_id="zulip").first()
user = get_user_by_delivery_email(registered_email, realm) user = get_user_by_delivery_email(registered_email, realm)
@ -110,7 +110,7 @@ def generate_all_emails(request: HttpRequest) -> HttpResponse:
"/json/invites", "/json/invites",
{ {
"invitee_emails": unregistered_email_2, "invitee_emails": unregistered_email_2,
"invite_expires_in_days": invite_expires_in_days, "invite_expires_in_minutes": invite_expires_in_minutes,
"stream_ids": orjson.dumps([stream.id]).decode(), "stream_ids": orjson.dumps([stream.id]).decode(),
}, },
**host_kwargs, **host_kwargs,

View File

@ -36,8 +36,8 @@ def invite_users_backend(
request: HttpRequest, request: HttpRequest,
user_profile: UserProfile, user_profile: UserProfile,
invitee_emails_raw: str = REQ("invitee_emails"), invitee_emails_raw: str = REQ("invitee_emails"),
invite_expires_in_days: Optional[int] = REQ( invite_expires_in_minutes: Optional[int] = REQ(
json_validator=check_none_or(check_int), default=settings.INVITATION_LINK_VALIDITY_DAYS json_validator=check_none_or(check_int), default=settings.INVITATION_LINK_VALIDITY_MINUTES
), ),
invite_as: int = REQ(json_validator=check_int, default=PreregistrationUser.INVITE_AS["MEMBER"]), invite_as: int = REQ(json_validator=check_int, default=PreregistrationUser.INVITE_AS["MEMBER"]),
stream_ids: List[int] = REQ(json_validator=check_list(check_int)), stream_ids: List[int] = REQ(json_validator=check_list(check_int)),
@ -80,7 +80,7 @@ def invite_users_backend(
user_profile, user_profile,
invitee_emails, invitee_emails,
streams, streams,
invite_expires_in_days=invite_expires_in_days, invite_expires_in_minutes=invite_expires_in_minutes,
invite_as=invite_as, invite_as=invite_as,
) )
return json_success(request) return json_success(request)
@ -174,8 +174,8 @@ def resend_user_invite_email(
def generate_multiuse_invite_backend( def generate_multiuse_invite_backend(
request: HttpRequest, request: HttpRequest,
user_profile: UserProfile, user_profile: UserProfile,
invite_expires_in_days: Optional[int] = REQ( invite_expires_in_minutes: Optional[int] = REQ(
json_validator=check_none_or(check_int), default=settings.INVITATION_LINK_VALIDITY_DAYS json_validator=check_none_or(check_int), default=settings.INVITATION_LINK_VALIDITY_MINUTES
), ),
invite_as: int = REQ(json_validator=check_int, default=PreregistrationUser.INVITE_AS["MEMBER"]), invite_as: int = REQ(json_validator=check_int, default=PreregistrationUser.INVITE_AS["MEMBER"]),
stream_ids: Sequence[int] = REQ(json_validator=check_list(check_int), default=[]), stream_ids: Sequence[int] = REQ(json_validator=check_list(check_int), default=[]),
@ -191,6 +191,6 @@ def generate_multiuse_invite_backend(
streams.append(stream) streams.append(stream)
invite_link = do_create_multiuse_invite_link( invite_link = do_create_multiuse_invite_link(
user_profile, invite_as, invite_expires_in_days, streams user_profile, invite_as, invite_expires_in_minutes, streams
) )
return json_success(request, data={"invite_link": invite_link}) return json_success(request, data={"invite_link": invite_link})

View File

@ -438,9 +438,12 @@ class LoopQueueProcessingWorker(QueueProcessingWorker):
@assign_queue("invites") @assign_queue("invites")
class ConfirmationEmailWorker(QueueProcessingWorker): class ConfirmationEmailWorker(QueueProcessingWorker):
def consume(self, data: Mapping[str, Any]) -> None: def consume(self, data: Mapping[str, Any]) -> None:
invite_expires_in_days = data["invite_expires_in_days"] if "invite_expires_in_days" in data:
invite_expires_in_minutes = data["invite_expires_in_days"] * 24 * 60
elif "invite_expires_in_minutes" in data:
invite_expires_in_minutes = data["invite_expires_in_minutes"]
invitee = filter_to_valid_prereg_users( invitee = filter_to_valid_prereg_users(
PreregistrationUser.objects.filter(id=data["prereg_id"]), invite_expires_in_days PreregistrationUser.objects.filter(id=data["prereg_id"]), invite_expires_in_minutes
).first() ).first()
if invitee is None: if invitee is None:
# The invitation could have been revoked # The invitation could have been revoked
@ -456,9 +459,9 @@ class ConfirmationEmailWorker(QueueProcessingWorker):
email_language = referrer.realm.default_language email_language = referrer.realm.default_language
activate_url = do_send_confirmation_email( activate_url = do_send_confirmation_email(
invitee, referrer, email_language, invite_expires_in_days invitee, referrer, email_language, invite_expires_in_minutes
) )
if invite_expires_in_days is None: if invite_expires_in_minutes is None:
# We do not queue reminder email for never expiring # We do not queue reminder email for never expiring
# invitations. This is probably a low importance bug; it # invitations. This is probably a low importance bug; it
# would likely be more natural to send a reminder after 7 # would likely be more natural to send a reminder after 7
@ -466,7 +469,7 @@ class ConfirmationEmailWorker(QueueProcessingWorker):
return return
# queue invitation reminder # queue invitation reminder
if invite_expires_in_days >= 4: if invite_expires_in_minutes >= 4 * 24 * 60:
context = common_context(referrer) context = common_context(referrer)
context.update( context.update(
activate_url=activate_url, activate_url=activate_url,
@ -481,7 +484,7 @@ class ConfirmationEmailWorker(QueueProcessingWorker):
from_address=FromAddress.tokenized_no_reply_placeholder, from_address=FromAddress.tokenized_no_reply_placeholder,
language=email_language, language=email_language,
context=context, context=context,
delay=datetime.timedelta(days=invite_expires_in_days - 2), delay=datetime.timedelta(minutes=invite_expires_in_minutes - (2 * 24 * 60)),
) )

View File

@ -38,6 +38,7 @@ from .configured_settings import (
EXTERNAL_URI_SCHEME, EXTERNAL_URI_SCHEME,
EXTRA_INSTALLED_APPS, EXTRA_INSTALLED_APPS,
GOOGLE_OAUTH2_CLIENT_ID, GOOGLE_OAUTH2_CLIENT_ID,
INVITATION_LINK_VALIDITY_DAYS,
IS_DEV_DROPLET, IS_DEV_DROPLET,
LOCAL_UPLOADS_DIR, LOCAL_UPLOADS_DIR,
MEMCACHED_LOCATION, MEMCACHED_LOCATION,
@ -1198,6 +1199,9 @@ AUTH_LDAP_BIND_PASSWORD = get_secret("auth_ldap_bind_password", "")
# MISC SETTINGS # MISC SETTINGS
######################################################################## ########################################################################
# Convert INVITATION_LINK_VALIDITY_DAYS into minutes.
INVITATION_LINK_VALIDITY_MINUTES = 24 * 60 * INVITATION_LINK_VALIDITY_DAYS
if PRODUCTION: if PRODUCTION:
# Filter out user data # Filter out user data
DEFAULT_EXCEPTION_REPORTER_FILTER = "zerver.filters.ZulipExceptionReporterFilter" DEFAULT_EXCEPTION_REPORTER_FILTER = "zerver.filters.ZulipExceptionReporterFilter"