Page MenuHomePhorge

D8377.1768373276.diff
No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None

D8377.1768373276.diff

diff --git a/keyserver/src/push/rescind.js b/keyserver/src/push/rescind.js
--- a/keyserver/src/push/rescind.js
+++ b/keyserver/src/push/rescind.js
@@ -3,11 +3,21 @@
import apn from '@parse/node-apn';
import invariant from 'invariant';
+import { NEXT_CODE_VERSION } from 'lib/shared/version-utils.js';
import { threadSubscriptions } from 'lib/types/subscription-types.js';
import { threadPermissions } from 'lib/types/thread-permission-types.js';
import { promiseAll } from 'lib/utils/promises.js';
+import {
+ prepareEncryptedAndroidNotificationRescinds,
+ prepareEncryptedIOSNotifications,
+} from './crypto.js';
import { getAPNsNotificationTopic } from './providers.js';
+import type {
+ NotificationTargetDevice,
+ TargetedAndroidNotification,
+ TargetedAPNsNotification,
+} from './types.js';
import { apnPush, fcmPush } from './utils.js';
import createIDs from '../creators/id-creator.js';
import { dbQuery, SQL } from '../database/database.js';
@@ -37,85 +47,88 @@
fetchQuery.append(SQL` GROUP BY n.id, m.user`);
const [fetchResult] = await dbQuery(fetchQuery);
+ const allDeviceTokens = [];
+ for (const row of fetchResult) {
+ const rawDelivery = JSON.parse(row.delivery);
+ const deliveries = Array.isArray(rawDelivery) ? rawDelivery : [rawDelivery];
+
+ for (const delivery of deliveries) {
+ if (delivery.iosID || delivery.deviceType === 'ios') {
+ const deviceTokens = delivery.iosDeviceTokens ?? delivery.deviceTokens;
+ allDeviceTokens.push(...deviceTokens);
+ } else if (delivery.androidID || delivery.deviceType === 'android') {
+ const deviceTokens =
+ delivery.androidDeviceTokens ?? delivery.deviceTokens;
+ allDeviceTokens.push(...deviceTokens);
+ }
+ }
+ }
+ const deviceTokenToCookieID = await getDeviceTokenToCookieID(allDeviceTokens);
+
const deliveryPromises = {};
const notifInfo = {};
const rescindedIDs = [];
+
for (const row of fetchResult) {
const rawDelivery = JSON.parse(row.delivery);
const deliveries = Array.isArray(rawDelivery) ? rawDelivery : [rawDelivery];
const id = row.id.toString();
const threadID = row.thread.toString();
+
notifInfo[id] = {
userID: row.user.toString(),
threadID,
messageID: row.message.toString(),
};
+
for (const delivery of deliveries) {
- if (delivery.iosID && delivery.iosDeviceTokens) {
- // Old iOS
- const notification = prepareIOSNotification(
- delivery.iosID,
- row.unread_count,
- threadID,
- );
- const targetedNotifications = delivery.iosDeviceTokens.map(
- deviceToken => ({ deviceToken, notification }),
- );
- deliveryPromises[id] = apnPush({
- targetedNotifications,
- platformDetails: { platform: 'ios' },
- });
- } else if (delivery.androidID) {
- // Old Android
- const notification = prepareAndroidNotification(
- row.collapse_key ? row.collapse_key : id,
- row.unread_count,
- threadID,
- );
- deliveryPromises[id] = fcmPush({
- targetedNotifications: delivery.androidDeviceTokens.map(
- deviceToken => ({
- deviceToken,
- notification,
- }),
- ),
- codeVersion: null,
- });
- } else if (delivery.deviceType === 'ios') {
- // New iOS
- const { iosID, deviceTokens, codeVersion } = delivery;
- const notification = prepareIOSNotification(
- iosID,
- row.unread_count,
- threadID,
- codeVersion,
- );
- const targetedNotifications = deviceTokens.map(deviceToken => ({
+ if (delivery.iosID) {
+ const deviceTokens = delivery.iosDeviceTokens ?? delivery.deviceTokens;
+ const devices = deviceTokens.map(deviceToken => ({
deviceToken,
- notification,
+ cookieID: deviceTokenToCookieID[deviceToken],
}));
- deliveryPromises[id] = apnPush({
- targetedNotifications,
- platformDetails: { platform: 'ios', codeVersion },
- });
- } else if (delivery.deviceType === 'android') {
- // New Android
- const { deviceTokens, codeVersion } = delivery;
- const notification = prepareAndroidNotification(
- row.collapse_key ? row.collapse_key : id,
- row.unread_count,
- threadID,
- );
- deliveryPromises[id] = fcmPush({
- targetedNotifications: deviceTokens.map(deviceToken => ({
- deviceToken,
- notification,
- })),
- codeVersion,
- });
+ const deliveryPromise = (async () => {
+ const targetedNotifications = await prepareIOSNotification(
+ delivery.iosID,
+ row.unread_count,
+ threadID,
+ delivery.codeVersion,
+ devices,
+ );
+ return await apnPush({
+ targetedNotifications,
+ platformDetails: {
+ platform: 'ios',
+ codeVersion: delivery.codeVersion,
+ },
+ });
+ })();
+ deliveryPromises[id] = deliveryPromise;
+ } else if (delivery.androidID || delivery.deviceType === 'android') {
+ const deviceTokens =
+ delivery.androidDeviceTokens ?? delivery.deviceTokens;
+ const devices = deviceTokens.map(deviceToken => ({
+ deviceToken,
+ cookieID: deviceTokenToCookieID[deviceToken],
+ }));
+ const deliveryPromise = (async () => {
+ const targetedNotifications = await prepareAndroidNotification(
+ row.collapse_key ? row.collapse_key : id,
+ row.unread_count,
+ threadID,
+ delivery.codeVersion,
+ devices,
+ );
+ return await fcmPush({
+ targetedNotifications,
+ codeVersion: delivery.codeVersion,
+ });
+ })();
+ deliveryPromises[id] = deliveryPromise;
}
}
- rescindedIDs.push(row.id);
+ rescindedIDs.push(id);
}
const numRescinds = Object.keys(deliveryPromises).length;
@@ -165,18 +178,65 @@
}
}
-function prepareIOSNotification(
+async function getDeviceTokenToCookieID(
+ deviceTokens,
+): Promise<{ +[string]: string }> {
+ if (deviceTokens.length === 0) {
+ return {};
+ }
+ const deviceTokenToCookieID = {};
+ const fetchCookiesQuery = SQL`
+ SELECT id, device_token FROM cookies
+ WHERE device_token IN (${deviceTokens})
+ `;
+ const [fetchResult] = await dbQuery(fetchCookiesQuery);
+ for (const row of fetchResult) {
+ deviceTokenToCookieID[row.device_token.toString()] = row.id.toString();
+ }
+ return deviceTokenToCookieID;
+}
+
+async function conditionallyEncryptNotification<T>(
+ notification: T,
+ codeVersion: ?number,
+ devices: $ReadOnlyArray<NotificationTargetDevice>,
+ encryptCallback: (
+ cookieIDs: $ReadOnlyArray<string>,
+ notification: T,
+ ) => Promise<$ReadOnlyArray<T>>,
+): Promise<$ReadOnlyArray<{ +deviceToken: string, +notification: T }>> {
+ const deviceTokens = devices.map(({ deviceToken }) => deviceToken);
+ const shouldBeEncrypted = codeVersion && codeVersion > NEXT_CODE_VERSION;
+ if (!shouldBeEncrypted) {
+ return deviceTokens.map(deviceToken => ({
+ notification,
+ deviceToken,
+ }));
+ }
+ const notificationPromises = devices.map(({ cookieID, deviceToken }) =>
+ (async () => {
+ const [encryptedNotif] = await encryptCallback([cookieID], notification);
+ return {
+ notification: encryptedNotif,
+ deviceToken,
+ };
+ })(),
+ );
+ return await Promise.all(notificationPromises);
+}
+
+async function prepareIOSNotification(
iosID: string,
unreadCount: number,
threadID: string,
codeVersion: ?number,
-): apn.Notification {
+ devices: $ReadOnlyArray<NotificationTargetDevice>,
+): Promise<$ReadOnlyArray<TargetedAPNsNotification>> {
const notification = new apn.Notification();
notification.topic = getAPNsNotificationTopic({
platform: 'ios',
codeVersion: codeVersion ?? undefined,
});
-
// It was agreed to temporarily make even releases staff-only. This way
// we will be able to prevent shipping NSE functionality to public iOS
// users until it is thoroughly tested among staff members.
@@ -203,15 +263,22 @@
notificationId: iosID,
},
};
- return notification;
+ return await conditionallyEncryptNotification(
+ notification,
+ codeVersion,
+ devices,
+ prepareEncryptedIOSNotifications,
+ );
}
-function prepareAndroidNotification(
+async function prepareAndroidNotification(
notifID: string,
unreadCount: number,
threadID: string,
-): Object {
- return {
+ codeVersion: ?number,
+ devices: $ReadOnlyArray<NotificationTargetDevice>,
+): Promise<$ReadOnlyArray<TargetedAndroidNotification>> {
+ const notification = {
data: {
badge: unreadCount.toString(),
rescind: 'true',
@@ -220,6 +287,12 @@
threadID,
},
};
+ return conditionallyEncryptNotification(
+ notification,
+ codeVersion,
+ devices,
+ prepareEncryptedAndroidNotificationRescinds,
+ );
}
export { rescindPushNotifs };
diff --git a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
--- a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
+++ b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
@@ -78,12 +78,7 @@
@Override
public void onMessageReceived(RemoteMessage message) {
- String rescind = message.getData().get(RESCIND_KEY);
- if ("true".equals(rescind) &&
- android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
- handleNotificationRescind(message);
- }
-
+ Log.w("COMM", "Received notification");
if (message.getData().get(ENCRYPTED_PAYLOAD_KEY) != null) {
try {
message = this.decryptRemoteMessage(message);
@@ -103,6 +98,12 @@
"Received unencrypted notification for client with existing olm session for notifications");
}
+ String rescind = message.getData().get(RESCIND_KEY);
+ if ("true".equals(rescind) &&
+ android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+ handleNotificationRescind(message);
+ }
+
String badge = message.getData().get(BADGE_KEY);
if (badge != null) {
try {

File Metadata

Mime Type
text/plain
Expires
Wed, Jan 14, 6:47 AM (47 s ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5930842
Default Alt Text
D8377.1768373276.diff (10 KB)

Event Timeline