push_notifs: Support APNs token auth, as well as cert auth.

This will make it possible to send notifications to multiple
distinct app IDs over the same connection.
This commit is contained in:
Greg Price 2023-11-07 16:37:08 -08:00 committed by Tim Abbott
parent 1af1a6d7e9
commit ff32d51700
6 changed files with 36 additions and 15 deletions

1
.gitignore vendored
View File

@ -18,6 +18,7 @@
## Config files for the dev environment
/zproject/apns-dev.pem
/zproject/apns-dev-key.p8
/zproject/dev-secrets.conf
/tools/conf.ini
/tools/custom_provision

View File

@ -213,13 +213,23 @@ push notifications through the new app is quite straightforward:
[FCM push notifications](https://firebase.google.com/docs/cloud-messaging)
key in the Google Developer console and set `android_gcm_api_key` in
`/etc/zulip/zulip-secrets.conf` to that key.
- Register for a
[mobile push notification certificate][apple-docs]
from Apple's developer console. Set `APNS_SANDBOX=False` and
`APNS_CERT_FILE` to be the path of your APNS certificate file in
`/etc/zulip/settings.py`.
- In Apple's developer console, register a [token][apple-doc-token] or
[certificate][apple-doc-cert] for sending push notifications.
Then in `/etc/zulip/settings.py`, set `APNS_SANDBOX=False`, and:
- If using APNs [certificate-based authentication][apple-doc-cert],
set `APNS_CERT_FILE` to the path of your APNs certificate file.
- If using APNs [token-based authentication][apple-doc-token],
set `APNS_TOKEN_KEY_FILE` to the path of your APNs token key file,
`APNS_TOKEN_KEY_ID` to the corresponding 10-character key ID, and
`APNS_TEAM_ID` to your 10-character Apple team ID.
- Set the `APNS_TOPIC` setting to the ID for
your app (for the official Zulip apps, it's `org.zulip.Zulip`).
- Restart the Zulip server.
[apple-docs]: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html
[apple-doc-cert]: https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_certificate-based_connection_to_apns
[apple-doc-token]: https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns

View File

@ -145,13 +145,17 @@ class APNsContext:
loop: asyncio.AbstractEventLoop
def apns_enabled() -> bool:
return settings.APNS_TOKEN_KEY_FILE is not None or settings.APNS_CERT_FILE is not None
@lru_cache(maxsize=None)
def get_apns_context() -> Optional[APNsContext]:
# We lazily do this import as part of optimizing Zulip's base
# import time.
import aioapns
if settings.APNS_CERT_FILE is None: # nocoverage
if not apns_enabled(): # nocoverage
return None
# NB if called concurrently, this will make excess connections.
@ -171,6 +175,9 @@ def get_apns_context() -> Optional[APNsContext]:
async def make_apns() -> aioapns.APNs:
return aioapns.APNs(
client_cert=settings.APNS_CERT_FILE,
key=settings.APNS_TOKEN_KEY_FILE,
key_id=settings.APNS_TOKEN_KEY_ID,
team_id=settings.APNS_TEAM_ID,
topic=settings.APNS_TOPIC,
max_connection_attempts=APNS_MAX_RETRIES,
use_sandbox=settings.APNS_SANDBOX,
@ -181,10 +188,6 @@ def get_apns_context() -> Optional[APNsContext]:
return APNsContext(apns=apns, loop=loop)
def apns_enabled() -> bool:
return settings.APNS_CERT_FILE is not None
def modernize_apns_payload(data: Mapping[str, Any]) -> Mapping[str, Any]:
"""Take a payload in an unknown Zulip version's format, and return in current format."""
# TODO this isn't super robust as is -- if a buggy remote server

View File

@ -401,6 +401,9 @@ POST_MIGRATION_CACHE_FLUSHING = False
# rebuilding the mobile app with a different push notifications
# server.
APNS_CERT_FILE: Optional[str] = None
APNS_TOKEN_KEY_FILE: Optional[str] = None
APNS_TOKEN_KEY_ID = get_secret("apns_token_key_id", development_only=True)
APNS_TEAM_ID = get_secret("apns_team_id", development_only=True)
APNS_SANDBOX = True
APNS_TOPIC = "org.zulip.Zulip"
# ZULIP_IOS_APP_ID is obsolete

View File

@ -100,11 +100,14 @@ USING_PGROONGA = True
# Flush cache after migration.
POST_MIGRATION_CACHE_FLUSHING = True
# If a sandbox APNs cert is provided, use it.
# To create such a cert, see instructions at:
# If a sandbox APNs key or cert is provided, use it.
# To create such a key or cert, see instructions at:
# https://github.com/zulip/zulip-mobile/blob/main/docs/howto/push-notifications.md#ios
_candidate_apns_token_key_file = "zproject/apns-dev-key.p8"
_candidate_apns_cert_file = "zproject/apns-dev.pem"
if os.path.isfile(_candidate_apns_cert_file):
if os.path.isfile(_candidate_apns_token_key_file):
APNS_TOKEN_KEY_FILE = _candidate_apns_token_key_file
elif os.path.isfile(_candidate_apns_cert_file):
APNS_CERT_FILE = _candidate_apns_cert_file
# Don't require anything about password strength in development

View File

@ -156,7 +156,8 @@ INLINE_URL_EMBED_PREVIEW = False
HOME_NOT_LOGGED_IN = "/login/"
LOGIN_URL = "/accounts/login/"
# If dev_settings.py found a cert file to use here, ignore it.
# If dev_settings.py found a key or cert file to use here, ignore it.
APNS_TOKEN_KEY_FILE: Optional[str] = None
APNS_CERT_FILE: Optional[str] = None
# By default will not send emails when login occurs.