zilencer: Add last_request_datetime to RemoteRealm + RemoteZulipServer.

For the RemoteRealm case, we can only set this in endpoints where the
remote server sends us the realm_uuid. So we're missing that for the
endpoints:

- remotes/push/unregister and remotes/push/unregister/all
- remotes/push/test_notification

This should be added in a follow-up commit.
This commit is contained in:
Mateusz Mandera 2023-12-25 03:01:58 +01:00
parent ea697cdd93
commit 3dca333b8d
5 changed files with 75 additions and 9 deletions

View File

@ -606,6 +606,8 @@ class PushBouncerNotificationTest(BouncerTestCase):
def test_send_notification_endpoint(self) -> None:
hamlet = self.example_user("hamlet")
server = self.server
remote_realm = RemoteRealm.objects.get(server=server, uuid=hamlet.realm.uuid)
token = "aaaa"
android_tokens = []
uuid_android_tokens = []
@ -649,6 +651,8 @@ class PushBouncerNotificationTest(BouncerTestCase):
},
"gcm_options": {},
}
time_sent = now()
with mock.patch(
"zilencer.views.send_android_push_notification", return_value=2
) as android_push, mock.patch(
@ -656,6 +660,8 @@ class PushBouncerNotificationTest(BouncerTestCase):
) as apple_push, mock.patch(
"corporate.lib.stripe.RemoteServerBillingSession.current_count_for_billed_licenses",
return_value=10,
), time_machine.travel(
time_sent, tick=False
), self.assertLogs(
"zilencer.views", level="INFO"
) as logger:
@ -711,6 +717,11 @@ class PushBouncerNotificationTest(BouncerTestCase):
remote=server,
)
remote_realm.refresh_from_db()
server.refresh_from_db()
self.assertEqual(remote_realm.last_request_datetime, time_sent)
self.assertEqual(server.last_request_datetime, time_sent)
def test_send_notification_endpoint_on_free_plans(self) -> None:
hamlet = self.example_user("hamlet")
remote_server = self.server
@ -1221,12 +1232,14 @@ class PushBouncerNotificationTest(BouncerTestCase):
# normal setup.
update_remote_realm_data_for_server(self.server, get_realms_info_for_push_bouncer())
# Test that we can push more times
result = self.client_post(endpoint, {"token": token, **appid}, subdomain="zulip")
self.assert_json_success(result)
time_sent = now()
with time_machine.travel(time_sent, tick=False):
result = self.client_post(endpoint, {"token": token, **appid}, subdomain="zulip")
self.assert_json_success(result)
result = self.client_post(endpoint, {"token": token, **appid}, subdomain="zulip")
self.assert_json_success(result)
# Test that we can push more times
result = self.client_post(endpoint, {"token": token, **appid}, subdomain="zulip")
self.assert_json_success(result)
tokens = list(
RemotePushDeviceToken.objects.filter(
@ -1237,9 +1250,16 @@ class PushBouncerNotificationTest(BouncerTestCase):
self.assertEqual(tokens[0].kind, kind)
# These new registrations have .remote_realm set properly.
assert tokens[0].remote_realm is not None
self.assertEqual(tokens[0].remote_realm.uuid, user.realm.uuid)
remote_realm = tokens[0].remote_realm
self.assertEqual(remote_realm.uuid, user.realm.uuid)
self.assertEqual(tokens[0].ios_app_id, appid.get("appid"))
# Both RemoteRealm and RemoteZulipServer should have last_request_datetime
# updated.
self.assertEqual(remote_realm.last_request_datetime, time_sent)
server.refresh_from_db()
self.assertEqual(server.last_request_datetime, time_sent)
# User should have tokens for both devices now.
tokens = list(RemotePushDeviceToken.objects.filter(user_uuid=user.uuid, server=server))
self.assert_length(tokens, 2)
@ -4748,11 +4768,16 @@ class PushBouncerSignupTest(ZulipTestCase):
hostname="example.com",
contact_email="server-admin@example.com",
)
result = self.client_post("/api/v1/remotes/server/register", request)
time_sent = now()
with time_machine.travel(time_sent, tick=False):
result = self.client_post("/api/v1/remotes/server/register", request)
self.assert_json_success(result)
server = RemoteZulipServer.objects.get(uuid=zulip_org_id)
self.assertEqual(server.hostname, "example.com")
self.assertEqual(server.contact_email, "server-admin@example.com")
self.assertEqual(server.last_request_datetime, time_sent)
# Update our hostname
request = dict(
@ -4761,11 +4786,14 @@ class PushBouncerSignupTest(ZulipTestCase):
hostname="zulip.example.com",
contact_email="server-admin@example.com",
)
result = self.client_post("/api/v1/remotes/server/register", request)
with time_machine.travel(time_sent + timedelta(minutes=1), tick=False):
result = self.client_post("/api/v1/remotes/server/register", request)
self.assert_json_success(result)
server = RemoteZulipServer.objects.get(uuid=zulip_org_id)
self.assertEqual(server.hostname, "zulip.example.com")
self.assertEqual(server.contact_email, "server-admin@example.com")
self.assertEqual(server.last_request_datetime, time_sent + timedelta(minutes=1))
# Now test rotating our key
request = dict(
@ -4783,7 +4811,7 @@ class PushBouncerSignupTest(ZulipTestCase):
zulip_org_key = request["new_org_key"]
self.assertEqual(server.api_key, zulip_org_key)
# Update our hostname
# Update contact_email
request = dict(
zulip_org_id=zulip_org_id,
zulip_org_key=zulip_org_key,

View File

@ -8,6 +8,7 @@ from django.http import HttpRequest, HttpResponse
from django.urls import path
from django.urls.resolvers import URLPattern
from django.utils.crypto import constant_time_compare
from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from django.views.decorators.csrf import csrf_exempt
from typing_extensions import Concatenate, ParamSpec, override
@ -120,6 +121,10 @@ def authenticated_remote_server_view(
raise UnauthorizedError(e.msg)
rate_limit_remote_server(request, remote_server, domain="api_by_remote_server")
remote_server.last_request_datetime = timezone_now()
remote_server.save(update_fields=["last_request_datetime"])
return view_func(request, remote_server, *args, **kwargs)
return _wrapped_view_func

View File

@ -0,0 +1,22 @@
# Generated by Django 4.2.8 on 2023-12-25 00:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("zilencer", "0056_remoterealm_realm_locally_deleted"),
]
operations = [
migrations.AddField(
model_name="remoterealm",
name="last_request_datetime",
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name="remotezulipserver",
name="last_request_datetime",
field=models.DateTimeField(null=True),
),
]

View File

@ -47,6 +47,7 @@ class RemoteZulipServer(models.Model):
hostname = models.CharField(max_length=HOSTNAME_MAX_LENGTH)
contact_email = models.EmailField(blank=True, null=False)
last_updated = models.DateTimeField("last updated", auto_now=True)
last_request_datetime = models.DateTimeField(null=True)
last_version = models.CharField(max_length=VERSION_MAX_LENGTH, null=True)
last_api_feature_level = models.PositiveIntegerField(null=True)
@ -142,6 +143,7 @@ class RemoteRealm(models.Model):
# The fields below are analogical to RemoteZulipServer fields.
last_updated = models.DateTimeField("last updated", auto_now=True)
last_request_datetime = models.DateTimeField(null=True)
# Whether the realm registration has been deactivated.
registration_deactivated = models.BooleanField(default=False)

View File

@ -144,6 +144,7 @@ def register_remote_server(
"hostname": hostname,
"contact_email": contact_email,
"api_key": zulip_org_key,
"last_request_datetime": timezone_now(),
},
)
if created:
@ -163,6 +164,8 @@ def register_remote_server(
remote_server.contact_email = contact_email
if new_org_key is not None:
remote_server.api_key = new_org_key
remote_server.last_request_datetime = timezone_now()
remote_server.save()
return json_success(request, data={"created": created})
@ -207,6 +210,9 @@ def register_remote_push_device(
# We want to associate the RemotePushDeviceToken with the RemoteRealm.
kwargs["remote_realm_id"] = remote_realm.id
remote_realm.last_request_datetime = timezone_now()
remote_realm.save(update_fields=["last_request_datetime"])
try:
with transaction.atomic():
RemotePushDeviceToken.objects.create(
@ -505,6 +511,9 @@ def remote_server_notify_push(
increment=len(android_devices) + len(apple_devices),
)
remote_realm.last_request_datetime = timezone_now()
remote_realm.save(update_fields=["last_request_datetime"])
# Truncate incoming pushes to 200, due to APNs maximum message
# sizes; see handle_remove_push_notification for the version of
# this for notifications generated natively on the server. We