Commit Graph

265 Commits

Author SHA1 Message Date
Alex Vandiver
721fd26442 send_email: Set the Date header according to local enqueue time.
Email clients tend to sort emails by the "Date" header, which is not
when the email was received -- emails can be arbitrarily delayed
during relaying.  Messages without a Date header (as all Zulip
messages previously) have one inserted by the first mailserver they
encounter.  As there are now multiple email-sending queues, we would
like the view of the database, as presented by the emails that are
sent out, to be consistent based on the Date header, which may not be
the same as when the client gets the email in their inbox.

Insert a Date header of when the Zulip system inserted the data into
the local queue, as that encodes when the full information was pulled
from the database.  This also opens the door to multiple workers
servicing the email_senders queue, to limit backlogging during large
notifications, without having to worry about inconsistent delivery
order between those two workers.

Messages which are sent synchronously via `send_email()` get a Date
header of when we attempt to send the message; this is, in practice,
no different from Django's default behaviour of doing so, but makes
the behaviour slightly more consistent.
2025-03-10 16:48:08 -07:00
Mateusz Mandera
1532b87910 zilencer: Log bouncer-side info when RemoteRealm creation fails.
This should also be logged on the bouncer side, to give us better
debugging information when self-hosters run into this error.
2025-02-19 17:11:35 -08:00
Mateusz Mandera
ddcc36c3aa register_server: Improve UX with the "hostname already in use" error.
An even better way than the current json error message recommending the
--registration-transfer option is to return an appropriate error code
and have that get picked up by the register_server command.

The register_server command can then display a more comprehensive,
better formatted error message with proper whitespaces and a pointer to
the documentation.
2025-01-30 14:32:36 -08:00
Mateusz Mandera
7390eb2ed0 zilencer: Rename registration takeover to registration transfer.
This is the final naming that we want, compared to the naming we merged
in #32399.
Includes renaming the API endpoints, but that should be fine as the
original PR was just merged and this isn't deployed anywhere.
2025-01-30 14:32:36 -08:00
Mateusz Mandera
685e49d34d zilencer: Improve "hostname already exists" error in registration.
Users most likely to run into this will be the ones who are moving to a
new server, but keeping their original domain and thus just need to
transfer the registration.
2025-01-28 11:10:50 -08:00
Mateusz Mandera
4e22a79e6a zilencer: Add flow for a server to reclaim its registration.
If the server controls the registration's hostname, it can reclaim its
registration credentials. This is useful, because self-hosted admins
frequently lose the credentials when moving their Zulip server to a
different machine / deployment method.

The flow is the following:
1. The host sends a POST request to
   /api/v1/remotes/server/register/takeover.
2. The bouncer responds with a signed token.
3. The host prepares to serve this token at /api/v1/zulip-services/verify and
   sends a POST to /remotes/server/register/verify_challenge endpoint of
   the bouncer.
4. Upon receiving the POST request, the bouncer GETS
   https://{hostname}/api/v1/zulip-services/verify, verifies the secret and
   responds to the original POST with the registration credentials.
5. The host can now save these credentials to it zulip-secrets.conf file
   and thus regains its push notifications registration.

Includes a global rate limit on the usage of the /verify_challenge
endpoint, as it causes us to make outgoing requests.
2025-01-28 11:10:50 -08:00
Anders Kaseorg
f223251ffe requirements: Upgrade Python requirements.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2025-01-14 09:42:16 -08:00
Mateusz Mandera
99ea0255da validate_hostname_or_raise_error: Improve hostname validation.
Obviously, URLs with a path or query are not valid hostnames.
2025-01-13 18:34:20 -08:00
Mateusz Mandera
4196859cd6 zilencer: Extract validate_hostname_or_raise_error function. 2025-01-13 18:34:20 -08:00
Lauryn Menard
e65f3cf657 corporate: Create license ledger for automanaged plan migrations.
If we move a paid plan from a remote server to a remote realm, and
the plan has automated license management, then we create an updated
license ledger entry when we move the plan for the remote realm
billing data so that we have an accurate user count for licenses
when the plan is next invoiced.
2024-12-31 16:54:40 -08:00
Lauryn Menard
c01787f41a corporate: Clean up vestiges of migrating plans to multiple realms.
In commit ea863bab5b, handle_customer_migration_from_server_to_realm
was updated to only move a remote server's plan in the case that there
is only one remote realm on the server.
2024-12-31 16:54:40 -08:00
Prakhar Pratyush
9c9866461a transaction: Add durable=True to the outermost db transactions.
This commit adds `durable=True` to the outermost db transactions
created in the following:
* confirm_email_change
* handle_upload_pre_finish_hook
* deliver_scheduled_emails
* restore_data_from_archive
* do_change_realm_subdomain
* do_create_realm
* do_deactivate_realm
* do_reactivate_realm
* do_delete_user
* do_delete_user_preserving_messages
* create_stripe_customer
* process_initial_upgrade
* do_update_plan
* request_sponsorship
* upload_message_attachment
* register_remote_server
* do_soft_deactivate_users
* maybe_send_batched_emails

It helps to avoid creating unintended savepoints in the future.

This is as a part of our plan to explicitly mark all the
transaction.atomic calls with either 'savepoint=False' or
'durable=True' as required.

* 'savepoint=True' is used in special cases.
2024-11-05 17:58:47 -08:00
Prakhar Pratyush
c4f74f470d remote_server_post_analytics: Add durable=True to outermost transaction.
This commit adds 'durable=True' to the outermost transaction
in 'remote_server_post_analytics'.

It also adds 'savepoint=False' to inner transaction.atomic
decorator to avoid creating savepoint.

This is as a part of our plan to explicitly mark all the
transaction.atomic decorators with either 'savepoint=False' or
'durable=True' as required.

* 'savepoint=True' is used in special cases.
2024-11-01 16:41:15 -07:00
Prakhar Pratyush
0fb5657131 transaction: Add durable=True to outermost transaction.atomic decorator.
This commit adds 'durable=True' to the outermost transactions
of the following functions:
* do_create_multiuse_invite_link
* do_revoke_user_invite
* do_revoke_multi_use_invite
* sync_ldap_user_data
* do_reactivate_remote_server
* do_deactivate_remote_server
* bulk_handle_digest_email
* handle_customer_migration_from_server_to_realm
* add_reaction
* remove_reaction
* deactivate_user_group

It helps to avoid creating unintended savepoints in the future.

This is as a part of our plan to explicitly mark all the
transaction.atomic decorators with either 'savepoint=False' or
'durable=True' as required.

* 'savepoint=True' is used in special cases.
2024-11-01 16:41:15 -07:00
Tim Abbott
400ca2c8dd zilencer: Avoid repeated emails about locally deleted realms. 2024-10-15 13:26:48 -07:00
Anders Kaseorg
f0f048de69 corporate: Import corporate.lib.stripe lazily.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-09-24 18:18:26 -07:00
Lauryn Menard
00ecd4c8f0 audit-log: Move remote realm event types to AuditLogEventType enum.
Event types moved: REMOTE_REALM_VALUE_UPDATED,
REMOTE_PLAN_TRANSFERRED_SERVER_TO_REALM, REMOTE_REALM_LOCALLY_DELETED
REMOTE_REALM_LOCALLY_DELETED_RESTORED
2024-09-09 11:50:13 -07:00
Lauryn Menard
aad93e149e audit-log: Move remote server event types to AuditLogEventType enum.
Event types moved: REMOTE_SERVER_DEACTIVATED, REMOTE_SERVER_REACTIVATED
REMOTE_SERVER_PLAN_TYPE_CHANGED, REMOTE_SERVER_DISCOUNT_CHANGED
REMOTE_SERVER_SPONSORSHIP_APPROVED, REMOTE_SERVER_BILLING_MODALITY_CHANGED
REMOTE_SERVER_SPONSORSHIP_PENDING_STATUS_CHANGED, REMOTE_SERVER_CREATED
2024-09-09 11:50:13 -07:00
Kenneth Rodrigues
4e63daf2ce zilencer: Migrate to typed_endpoint. 2024-07-31 17:10:06 -07:00
Alex Vandiver
14a2b5473f zilencer: Avoid repeated COUNT(*) queries.
Because Django does not support returning the inserted row-ids with a
`bulk_create(..., ignore_conflicts=True)`, we previously counted the
total rows before and after insertion.  This is rather inefficient,
and can lead to database contention when many servers are reporting
statistics at once.

Switch to reaching into the private `_insert` method, which does
support what we need.  While relying a private method is poor form, it
is mildly preferable to attempting to re-implement all of the
complexities of it.
2024-07-31 09:19:31 -07:00
Anders Kaseorg
0fa5e7f629 ruff: Fix UP035 Import from collections.abc, typing instead.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-07-13 22:28:22 -07:00
Anders Kaseorg
531b34cb4c ruff: Fix UP007 Use X | Y for type annotations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-07-13 22:28:22 -07:00
Anders Kaseorg
e08a24e47f ruff: Fix UP006 Use list instead of List for type annotation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-07-13 22:28:22 -07:00
Mateusz Mandera
f8616fa013 analytics: Send ZULIP_MERGE_BASE to the bouncer. 2024-06-23 07:44:11 -07:00
Mateusz Mandera
4917e01ffb push_notifications: Migrate to FCM HTTP v1 API.
The legacy API we use via python-gcm is deprecated and about to be
disabled.

Fixes #29768.
2024-06-17 18:26:26 -07:00
Mateusz Mandera
00b8cce50e push_notifs: Rename PushDeviceToken.GCM to FCM. 2024-06-17 18:22:59 -07:00
Alex Vandiver
51b7a2fb23 zilencer: Do not error on old stats which we no longer have. 2024-06-06 13:26:21 -04:00
Alex Vandiver
04450389e1 push_notifications: Provide a stable ordering.
Otherwise, this can cause test failures based on internals of the database.
2024-06-05 11:48:27 -07:00
Alex Vandiver
6c17cca208 zilencer: Drop unwanted data that old servers might still send. 2024-06-03 12:35:35 -07:00
Alex Vandiver
e5a0b3b3c5 zilencer: Disambiguate no MX records from the domain not existing.
This switches to dnspython, since it offers the higher-level
`resolve_name` method to look up both A and AAAA records, and set
timeouts.
2024-05-24 09:34:31 -07:00
Alex Vandiver
9388805471 zilencer: Fix typo in variable name. 2024-05-24 09:34:31 -07:00
Alex Vandiver
9d66a8eb73 zilencer: Give a better error message for foo@example.com emails. 2024-05-24 09:34:31 -07:00
Anders Kaseorg
b545abe1e2 typos: Fix typos caught by mwic.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-05-20 13:55:00 -07:00
Kenneth Rodrigues
326944542e views: Use ApnsAppId for validation. 2024-04-21 11:09:32 -07:00
Mahhheshh
d82f33a3c8 zilencer: Migrate to @typed_endpoint.
migrated views:
- `zilencer.views.register_remote_server`
- `zilencer.views.register_remote_push_device`
- `zilencer.views.unregister_remote_push_device`
- `zilencer.views.unregister_all_remote_push_devices`
- `zilencer.views.remote_server_notify_push`

to make sure the previous checks for `remote_server_notify_push` matches
to old one, The `RemoteServerNotificationPayload` is defined.
2024-04-17 13:45:54 -07:00
Alex Vandiver
936c2b54cb push_notifications: Use ignore_conflicts, over catching IntegrityError.
The IntegrityError shows up in the database logs, which looks
unnecessarily concerning.  Use `ON CONFLICT IGNORE` to mark this as
expected, especially since the return value is never used.
2024-04-12 11:36:40 -07:00
Alex Vandiver
9e28619727 zilencer: Fix typo in comment. 2024-04-12 11:36:40 -07:00
Mateusz Mandera
374178903e remote_billing: Move stripe_customer_id from server to realm if needed.
As explained in the comment, when we're moving the server plan to the
remote realm's Customer object, the realm Customer may not have
stripe_customer_id set and therefore that value needs to get moved from
the server Customer.
2024-03-29 16:27:03 -07:00
Prakhar Pratyush
d66b7ad853 zilencer: Notify when paid plan attached to now-deleted remote realm.
When a server doesn't submit a remote realm info which was
previously submitted, we mark it as locally deleted.

If such a realm has paid plan attached to it, we should investigate.

This commit adds logic to send an email to sales@zulip.com for
investigation.
2024-02-29 12:50:23 -08:00
Alex Vandiver
4b512b3409 email_validation: Rename to validate_is_not_disposable.
This clarifies what we are asserting -- namely, that the email is
_not_ disposable.
2024-02-27 10:19:23 -08:00
Mateusz Mandera
32837a8ae4 update_remote_realm_data_for_server: Rename uuids to reported_uuids.
uuids is a bit too generic, the rename makes it clearer what exactly is
being referenced conceptually.
2024-02-24 08:52:11 -08:00
Mateusz Mandera
d7cf4336e4 zilencer: Flip realm_locally_deleted when realm re-appears during sync. 2024-02-24 08:52:11 -08:00
Mateusz Mandera
cb2d35cf27 zilencer: Fix incorrect registration_deactivated mentions in realm sync.
We no longer modify this flag upon encountering a locally deleted realm,
as of 880133295e
2024-02-24 08:52:11 -08:00
Tim Abbott
bd0b5d80ba zilencer: Use early return pattern for readability. 2024-02-21 11:10:45 -08:00
Mateusz Mandera
ea863bab5b remote_billing: Remove code migrating Legacy plan from server to realms.
We no longer want to migrate Legacy plans from server to realms, since
Legacy plans are not really a thing in the original sense anymore, since
February 15th.

Now they're just a tool to give temporary extensions of access to the
push notification service for users, when needed. And as such, it makes
no sense to migrate like that.

The remaining code in this function is for migrating (any) plan from the
server object to the realm object, if the server has just a single
realm.
2024-02-21 11:10:45 -08:00
Mateusz Mandera
834dbd552b remote_billing: Make handle_customer_migration_... more robust.
The logic in the case where there's only one realm and the function
tries to migrate the server's plan to it, had two main unhandled edge
cases that would throw exceptions:
1.
```
        remote_realm = RemoteRealm.objects.get(
            uuid=realm_uuids[0], plan_type=RemoteRealm.PLAN_TYPE_SELF_MANAGED
        )
```

This could throw an exception if the RemoteRealm exists, but has an
active e.g. Legacy plan. Then there'd be no object matching the
plan_type in the query, raising RemoteRealm.DoesNotExist.

2. If the RemoteRealm had e.g. a Legacy plan in the past, that's now
   expired, then it'd have a Customer object. Meaning that the attempt
   to move the server's customer to the realm:
   `server_plan.customer = remote_realm_customer`
   would trigger an IntegrityError since a RemoteRealm can't have two
   Customer objects.

In simple cases the situation in (2) can still be easily migrated, by
moving the plan from the server's customer to the realm's customer.
2024-02-20 16:02:03 -08:00
Mateusz Mandera
616527e73e zilencer: Exclude realm_locally_deleted in get_human_user_realm_uuids.
Just like deactivated realms should be excluded, so should locally
deleted realms.

In particular, failure to exclude locally deleted realms breaks
handle_customer_migration_from_server_to_realms.
2024-02-19 20:27:24 -08:00
Mateusz Mandera
3bda31c48c zilencer: Improve json error when plan doesn't allow push notifs.
This allows the self-hosted server to explicitly test for that exception
and catch and log it nicely.
2024-02-07 10:36:33 -08:00
Mateusz Mandera
4a245a3f62 register_remote_server: Add more validation of contact_email. 2024-01-26 09:09:31 -08:00
Mateusz Mandera
25f47bd749 register_remote_server: Don't allow duplicate hostnames.
This requires a bit of restructuring of the existing code to handle all
the cases correctly.
2024-01-26 09:09:31 -08:00