Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F33012604
D8377.1768373276.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D8377.1768373276.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8377: Encrypt rescinds
Attached
Detach File
Event Timeline
Log In to Comment