Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32222537
D12397.1765203845.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
48 KB
Referenced Files
None
Subscribers
None
D12397.1765203845.diff
View Options
diff --git a/keyserver/src/push/crypto.js b/keyserver/src/push/crypto.js
--- a/keyserver/src/push/crypto.js
+++ b/keyserver/src/push/crypto.js
@@ -6,15 +6,6 @@
import _cloneDeep from 'lodash/fp/cloneDeep.js';
import type {
- PlainTextWebNotification,
- PlainTextWebNotificationPayload,
- WebNotification,
- PlainTextWNSNotification,
- WNSNotification,
- AndroidVisualNotification,
- AndroidVisualNotificationPayload,
- AndroidBadgeOnlyNotification,
- AndroidNotificationRescind,
NotificationTargetDevice,
SenderDeviceDescriptor,
EncryptedNotifUtilsAPI,
@@ -132,245 +123,6 @@
}
}
-async function encryptAndroidNotificationPayload<T>(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- cookieID: string,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- unencryptedPayload: T,
- payloadSizeValidator?: (
- T | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
- ) => boolean,
-): Promise<{
- +resultPayload:
- | T
- | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
- +payloadSizeExceeded: boolean,
- +encryptionOrder?: number,
-}> {
- try {
- const unencryptedSerializedPayload = JSON.stringify(unencryptedPayload);
- if (!unencryptedSerializedPayload) {
- return {
- resultPayload: unencryptedPayload,
- payloadSizeExceeded: payloadSizeValidator
- ? payloadSizeValidator(unencryptedPayload)
- : false,
- };
- }
-
- let dbPersistCondition;
- if (payloadSizeValidator) {
- dbPersistCondition = (serializedPayload: string) =>
- payloadSizeValidator({
- encryptedPayload: serializedPayload,
- ...senderDeviceDescriptor,
- });
- }
-
- const {
- encryptedData: serializedPayload,
- sizeLimitViolated: dbPersistConditionViolated,
- encryptionOrder,
- } = await encryptedNotifUtilsAPI.encryptSerializedNotifPayload(
- cookieID,
- unencryptedSerializedPayload,
- dbPersistCondition,
- );
-
- return {
- resultPayload: {
- encryptedPayload: serializedPayload.body,
- ...senderDeviceDescriptor,
- },
- payloadSizeExceeded: !!dbPersistConditionViolated,
- encryptionOrder,
- };
- } catch (e) {
- console.log('Notification encryption failed: ' + e);
- const resultPayload = {
- encryptionFailed: '1',
- ...unencryptedPayload,
- };
- return {
- resultPayload,
- payloadSizeExceeded: payloadSizeValidator
- ? payloadSizeValidator(resultPayload)
- : false,
- };
- }
-}
-
-async function encryptAndroidVisualNotification(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- cookieID: string,
- notification: AndroidVisualNotification,
- notificationSizeValidator?: AndroidVisualNotification => boolean,
- blobHolder?: ?string,
-): Promise<{
- +notification: AndroidVisualNotification,
- +payloadSizeExceeded: boolean,
- +encryptionOrder?: number,
-}> {
- const { id, ...rest } = notification.data;
-
- let unencryptedData = {};
- if (id) {
- unencryptedData = { id };
- }
-
- let unencryptedPayload = rest;
- if (blobHolder) {
- unencryptedPayload = { ...unencryptedPayload, blobHolder };
- }
-
- let payloadSizeValidator;
- if (notificationSizeValidator) {
- payloadSizeValidator = (
- payload:
- | AndroidVisualNotificationPayload
- | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
- ) => {
- return notificationSizeValidator({
- data: { ...unencryptedData, ...payload },
- });
- };
- }
- const { resultPayload, payloadSizeExceeded, encryptionOrder } =
- await encryptAndroidNotificationPayload(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- unencryptedPayload,
- payloadSizeValidator,
- );
- return {
- notification: {
- data: {
- ...unencryptedData,
- ...resultPayload,
- },
- },
- payloadSizeExceeded,
- encryptionOrder,
- };
-}
-
-async function encryptAndroidSilentNotification(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- cookieID: string,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
-): Promise<AndroidNotificationRescind | AndroidBadgeOnlyNotification> {
- // We don't validate payload size for rescind
- // since they are expected to be small and
- // never exceed any FCM limit
- const { ...unencryptedPayload } = notification.data;
- const { resultPayload } = await encryptAndroidNotificationPayload(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- unencryptedPayload,
- );
- if (resultPayload.encryptedPayload) {
- return {
- data: { ...resultPayload },
- };
- }
-
- if (resultPayload.rescind) {
- return {
- data: { ...resultPayload },
- };
- }
-
- return {
- data: {
- ...resultPayload,
- },
- };
-}
-
-async function encryptBasicPayload<T>(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- cookieID: string,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- basicPayload: T,
-): Promise<
- | $ReadOnly<{
- ...SenderDeviceDescriptor,
- +encryptedPayload: string,
- +encryptionOrder?: number,
- }>
- | { ...T, +encryptionFailed: '1' },
-> {
- const unencryptedSerializedPayload = JSON.stringify(basicPayload);
-
- if (!unencryptedSerializedPayload) {
- return { ...basicPayload, encryptionFailed: '1' };
- }
-
- try {
- const { encryptedData: serializedPayload, encryptionOrder } =
- await encryptedNotifUtilsAPI.encryptSerializedNotifPayload(
- cookieID,
- unencryptedSerializedPayload,
- );
-
- return {
- ...senderDeviceDescriptor,
- encryptedPayload: serializedPayload.body,
- encryptionOrder,
- };
- } catch (e) {
- console.log('Notification encryption failed: ' + e);
- return {
- ...basicPayload,
- encryptionFailed: '1',
- };
- }
-}
-
-async function encryptWebNotification(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- cookieID: string,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- notification: PlainTextWebNotification,
-): Promise<{ +notification: WebNotification, +encryptionOrder?: number }> {
- const { id, ...payloadSansId } = notification;
- const { encryptionOrder, ...encryptionResult } =
- await encryptBasicPayload<PlainTextWebNotificationPayload>(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- payloadSansId,
- );
-
- return {
- notification: { id, ...encryptionResult },
- encryptionOrder,
- };
-}
-
-async function encryptWNSNotification(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- cookieID: string,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- notification: PlainTextWNSNotification,
-): Promise<{ +notification: WNSNotification, +encryptionOrder?: number }> {
- const { encryptionOrder, ...encryptionResult } =
- await encryptBasicPayload<PlainTextWNSNotification>(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- notification,
- );
- return {
- notification: { ...encryptionResult },
- encryptionOrder,
- };
-}
-
function prepareEncryptedAPNsNotifications(
encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
senderDeviceDescriptor: SenderDeviceDescriptor,
@@ -433,118 +185,6 @@
return Promise.all(notificationPromises);
}
-function prepareEncryptedAndroidVisualNotifications(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- devices: $ReadOnlyArray<NotificationTargetDevice>,
- notification: AndroidVisualNotification,
- notificationSizeValidator?: (
- notification: AndroidVisualNotification,
- ) => boolean,
-): Promise<
- $ReadOnlyArray<{
- +cookieID: string,
- +deviceToken: string,
- +notification: AndroidVisualNotification,
- +payloadSizeExceeded: boolean,
- +encryptionOrder?: number,
- }>,
-> {
- const notificationPromises = devices.map(
- async ({ deviceToken, cookieID, blobHolder }) => {
- const notif = await encryptAndroidVisualNotification(
- encryptedNotifUtilsAPI,
- senderDeviceDescriptor,
- cookieID,
- notification,
- notificationSizeValidator,
- blobHolder,
- );
- return { deviceToken, cookieID, ...notif };
- },
- );
- return Promise.all(notificationPromises);
-}
-
-function prepareEncryptedAndroidSilentNotifications(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- devices: $ReadOnlyArray<NotificationTargetDevice>,
- notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
-): Promise<
- $ReadOnlyArray<{
- +cookieID: string,
- +deviceToken: string,
- +notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
- +encryptionOrder?: number,
- }>,
-> {
- const notificationPromises = devices.map(
- async ({ deviceToken, cookieID }) => {
- const notif = await encryptAndroidSilentNotification(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- notification,
- );
- return { deviceToken, cookieID, notification: notif };
- },
- );
- return Promise.all(notificationPromises);
-}
-
-function prepareEncryptedWebNotifications(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- devices: $ReadOnlyArray<NotificationTargetDevice>,
- notification: PlainTextWebNotification,
-): Promise<
- $ReadOnlyArray<{
- +deviceToken: string,
- +notification: WebNotification,
- +encryptionOrder?: number,
- }>,
-> {
- const notificationPromises = devices.map(
- async ({ deviceToken, cookieID }) => {
- const notif = await encryptWebNotification(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- notification,
- );
- return { ...notif, deviceToken };
- },
- );
- return Promise.all(notificationPromises);
-}
-
-function prepareEncryptedWNSNotifications(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- devices: $ReadOnlyArray<NotificationTargetDevice>,
- notification: PlainTextWNSNotification,
-): Promise<
- $ReadOnlyArray<{
- +deviceToken: string,
- +notification: WNSNotification,
- +encryptionOrder?: number,
- }>,
-> {
- const notificationPromises = devices.map(
- async ({ deviceToken, cookieID }) => {
- const notif = await encryptWNSNotification(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- notification,
- );
- return { ...notif, deviceToken };
- },
- );
- return Promise.all(notificationPromises);
-}
-
async function encryptBlobPayload(payload: string): Promise<{
+encryptionKey: string,
+encryptedPayload: Blob,
@@ -574,9 +214,5 @@
export {
prepareEncryptedAPNsNotifications,
prepareEncryptedIOSNotificationRescind,
- prepareEncryptedAndroidVisualNotifications,
- prepareEncryptedAndroidSilentNotifications,
- prepareEncryptedWebNotifications,
- prepareEncryptedWNSNotifications,
encryptBlobPayload,
};
diff --git a/keyserver/src/push/encrypted-notif-utils-api.js b/keyserver/src/push/encrypted-notif-utils-api.js
--- a/keyserver/src/push/encrypted-notif-utils-api.js
+++ b/keyserver/src/push/encrypted-notif-utils-api.js
@@ -11,7 +11,10 @@
encryptSerializedNotifPayload: async (
cryptoID: string,
unencryptedPayload: string,
- encryptedPayloadSizeValidator?: (encryptedPayload: string) => boolean,
+ encryptedPayloadSizeValidator?: (
+ encryptedPayload: string,
+ type: '1' | '0',
+ ) => boolean,
) => {
let dbPersistCondition;
if (encryptedPayloadSizeValidator) {
@@ -19,7 +22,11 @@
serializedPayload,
}: {
+[string]: EncryptResult,
- }) => encryptedPayloadSizeValidator(serializedPayload.body);
+ }) =>
+ encryptedPayloadSizeValidator(
+ serializedPayload.body,
+ serializedPayload.type ? '1' : '0',
+ );
}
const {
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
@@ -5,6 +5,7 @@
import type { FirebaseError } from 'firebase-admin';
import invariant from 'invariant';
+import { prepareEncryptedAndroidSilentNotifications } from 'lib/push/crypto.js';
import type { PlatformDetails } from 'lib/types/device-types.js';
import type {
NotificationTargetDevice,
@@ -17,10 +18,7 @@
import { promiseAll } from 'lib/utils/promises.js';
import { tID } from 'lib/utils/validation-utils.js';
-import {
- prepareEncryptedAndroidSilentNotifications,
- prepareEncryptedIOSNotificationRescind,
-} from './crypto.js';
+import { prepareEncryptedIOSNotificationRescind } from './crypto.js';
import encryptedNotifUtilsAPI from './encrypted-notif-utils-api.js';
import { getAPNsNotificationTopic } from './providers.js';
import type { TargetedAPNsNotification } from './types.js';
diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js
--- a/keyserver/src/push/send.js
+++ b/keyserver/src/push/send.js
@@ -12,6 +12,22 @@
import t from 'tcomb';
import uuidv4 from 'uuid/v4.js';
+import {
+ type AndroidNotifInputData,
+ androidNotifInputDataValidator,
+ createAndroidVisualNotification,
+} from 'lib/push/android-notif-creators.js';
+import { prepareEncryptedAndroidSilentNotifications } from 'lib/push/crypto.js';
+import {
+ type WebNotifInputData,
+ webNotifInputDataValidator,
+ createWebNotification,
+} from 'lib/push/web-notif-creators.js';
+import {
+ type WNSNotifInputData,
+ wnsNotifInputDataValidator,
+ createWNSNotification,
+} from 'lib/push/wns-notif-creators.js';
import { oldValidUsernameRegex } from 'lib/shared/account-utils.js';
import { isUserMentioned } from 'lib/shared/mention-utils.js';
import {
@@ -35,7 +51,6 @@
} from 'lib/types/message-types.js';
import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
import type {
- AndroidVisualNotification,
NotificationTargetDevice,
TargetedAndroidNotification,
TargetedWebNotification,
@@ -49,13 +64,7 @@
import { values } from 'lib/utils/objects.js';
import { tID, tPlatformDetails, tShape } from 'lib/utils/validation-utils.js';
-import {
- prepareEncryptedAndroidVisualNotifications,
- prepareEncryptedAndroidSilentNotifications,
- prepareEncryptedAPNsNotifications,
- prepareEncryptedWebNotifications,
- prepareEncryptedWNSNotifications,
-} from './crypto.js';
+import { prepareEncryptedAPNsNotifications } from './crypto.js';
import encryptedNotifUtilsAPI from './encrypted-notif-utils-api.js';
import { getAPNsNotificationTopic } from './providers.js';
import { rescindPushNotifs } from './rescind.js';
@@ -63,12 +72,10 @@
import {
apnMaxNotificationPayloadByteSize,
apnPush,
- fcmMaxNotificationPayloadByteSize,
fcmPush,
getUnreadCounts,
webPush,
type WebPushError,
- wnsMaxNotificationPayloadByteSize,
wnsPush,
type WNSPushError,
} from './utils.js';
@@ -370,7 +377,7 @@
(async () => {
const targetedNotifications = await prepareAndroidVisualNotification(
{
- keyserverID,
+ senderDeviceDescriptor: { keyserverID },
notifTexts,
newRawMessageInfos: shimmedNewRawMessageInfos,
threadID: threadInfo.id,
@@ -378,7 +385,7 @@
badgeOnly,
unreadCount,
platformDetails,
- dbID,
+ notifID: dbID,
},
devices,
);
@@ -411,9 +418,10 @@
{
notifTexts,
threadID: threadInfo.id,
- keyserverID,
+ senderDeviceDescriptor: { keyserverID },
unreadCount,
platformDetails,
+ id: uuidv4(),
},
devices,
);
@@ -491,7 +499,7 @@
const targetedNotifications = await prepareWNSNotification(devices, {
notifTexts,
threadID: threadInfo.id,
- keyserverID,
+ senderDeviceDescriptor: { keyserverID },
unreadCount,
platformDetails,
});
@@ -1161,14 +1169,6 @@
];
}
-type AndroidNotifInputData = {
- ...CommonNativeNotifInputData,
- +dbID: string,
-};
-const androidNotifInputDataValidator = tShape<AndroidNotifInputData>({
- ...commonNativeNotifInputDataValidator.meta.props,
- dbID: t.String,
-});
async function prepareAndroidVisualNotification(
inputData: AndroidNotifInputData,
devices: $ReadOnlyArray<NotificationTargetDevice>,
@@ -1178,203 +1178,14 @@
androidNotifInputDataValidator,
inputData,
);
- const {
- keyserverID,
- notifTexts,
- newRawMessageInfos,
- threadID,
- collapseKey,
- badgeOnly,
- unreadCount,
- platformDetails,
- dbID,
- } = convertedData;
-
- const canDecryptNonCollapsibleTextNotifs = hasMinCodeVersion(
- platformDetails,
- { native: 228 },
- );
- const isNonCollapsibleTextNotif =
- newRawMessageInfos.every(
- newRawMessageInfo => newRawMessageInfo.type === messageTypes.TEXT,
- ) && !collapseKey;
-
- const canDecryptAllNotifTypes = hasMinCodeVersion(platformDetails, {
- native: 267,
- });
-
- const shouldBeEncrypted =
- canDecryptAllNotifTypes ||
- (canDecryptNonCollapsibleTextNotifs && isNonCollapsibleTextNotif);
-
- const { merged, ...rest } = notifTexts;
- const notification = {
- data: {
- badge: unreadCount.toString(),
- ...rest,
- threadID,
- },
- };
-
- let notifID;
- if (collapseKey && canDecryptAllNotifTypes) {
- notifID = dbID;
- notification.data = {
- ...notification.data,
- collapseKey,
- };
- } else if (collapseKey) {
- notifID = collapseKey;
- } else {
- notifID = dbID;
- }
-
- notification.data = {
- ...notification.data,
- id: notifID,
- badgeOnly: badgeOnly ? '1' : '0',
- };
-
- const messageInfos = JSON.stringify(newRawMessageInfos);
- const copyWithMessageInfos = {
- ...notification,
- data: { ...notification.data, messageInfos },
- };
-
- const priority = 'high';
- if (!shouldBeEncrypted) {
- const notificationToSend =
- encryptedNotifUtilsAPI.getNotifByteSize(
- JSON.stringify(copyWithMessageInfos),
- ) <= fcmMaxNotificationPayloadByteSize
- ? copyWithMessageInfos
- : notification;
-
- return devices.map(({ deviceToken }) => ({
- priority,
- notification: notificationToSend,
- deviceToken,
- }));
- }
-
- const notificationsSizeValidator = (notif: AndroidVisualNotification) => {
- const serializedNotif = JSON.stringify(notif);
- return (
- !serializedNotif ||
- encryptedNotifUtilsAPI.getNotifByteSize(serializedNotif) <=
- fcmMaxNotificationPayloadByteSize
- );
- };
-
- const notifsWithMessageInfos =
- await prepareEncryptedAndroidVisualNotifications(
- encryptedNotifUtilsAPI,
- { keyserverID },
- devices,
- copyWithMessageInfos,
- notificationsSizeValidator,
- );
-
- const devicesWithExcessiveSizeNoHolders = notifsWithMessageInfos
- .filter(({ payloadSizeExceeded }) => payloadSizeExceeded)
- .map(({ cookieID, deviceToken }) => ({ cookieID, deviceToken }));
-
- if (devicesWithExcessiveSizeNoHolders.length === 0) {
- return notifsWithMessageInfos.map(
- ({ notification: notif, deviceToken, encryptionOrder }) => ({
- priority,
- notification: notif,
- deviceToken,
- encryptionOrder,
- }),
- );
- }
-
- const canQueryBlobService = hasMinCodeVersion(platformDetails, {
- native: 331,
- });
-
- let blobHash, blobHolders, encryptionKey, blobUploadError;
- if (canQueryBlobService) {
- ({ blobHash, blobHolders, encryptionKey, blobUploadError } =
- await encryptedNotifUtilsAPI.uploadLargeNotifPayload(
- JSON.stringify(copyWithMessageInfos.data),
- devicesWithExcessiveSizeNoHolders.length,
- ));
- }
-
- if (blobUploadError) {
- console.warn(
- `Failed to upload payload of notification: ${notifID} ` +
- `due to error: ${blobUploadError}`,
- );
- }
-
- let devicesWithExcessiveSize = devicesWithExcessiveSizeNoHolders;
- if (
- blobHash &&
- encryptionKey &&
- blobHolders &&
- blobHolders.length === devicesWithExcessiveSizeNoHolders.length
- ) {
- notification.data = {
- ...notification.data,
- blobHash,
- encryptionKey,
- };
-
- devicesWithExcessiveSize = blobHolders.map((holder, idx) => ({
- ...devicesWithExcessiveSize[idx],
- blobHolder: holder,
- }));
- }
- const notifsWithoutMessageInfos =
- await prepareEncryptedAndroidVisualNotifications(
- encryptedNotifUtilsAPI,
- { keyserverID },
- devicesWithExcessiveSize,
- notification,
- );
-
- const targetedNotifsWithMessageInfos = notifsWithMessageInfos
- .filter(({ payloadSizeExceeded }) => !payloadSizeExceeded)
- .map(({ notification: notif, deviceToken, encryptionOrder }) => ({
- priority,
- notification: notif,
- deviceToken,
- encryptionOrder,
- }));
-
- const targetedNotifsWithoutMessageInfos = notifsWithoutMessageInfos.map(
- ({ notification: notif, deviceToken, encryptionOrder }) => ({
- priority,
- notification: notif,
- deviceToken,
- encryptionOrder,
- }),
+ return createAndroidVisualNotification(
+ encryptedNotifUtilsAPI,
+ convertedData,
+ devices,
);
-
- return [
- ...targetedNotifsWithMessageInfos,
- ...targetedNotifsWithoutMessageInfos,
- ];
}
-type WebNotifInputData = {
- +notifTexts: ResolvedNotifTexts,
- +threadID: string,
- +keyserverID: string,
- +unreadCount: number,
- +platformDetails: PlatformDetails,
-};
-const webNotifInputDataValidator = tShape<WebNotifInputData>({
- notifTexts: resolvedNotifTextsValidator,
- threadID: tID,
- keyserverID: t.String,
- unreadCount: t.Number,
- platformDetails: tPlatformDetails,
-});
async function prepareWebNotification(
inputData: WebNotifInputData,
devices: $ReadOnlyArray<NotificationTargetDevice>,
@@ -1384,46 +1195,10 @@
webNotifInputDataValidator,
inputData,
);
- const { notifTexts, threadID, unreadCount, keyserverID } = convertedData;
- const id = uuidv4();
- const { merged, ...rest } = notifTexts;
- const notification = {
- ...rest,
- unreadCount,
- id,
- threadID,
- };
-
- const shouldBeEncrypted = hasMinCodeVersion(convertedData.platformDetails, {
- web: 43,
- });
-
- if (!shouldBeEncrypted) {
- return devices.map(({ deviceToken }) => ({ deviceToken, notification }));
- }
- return prepareEncryptedWebNotifications(
- encryptedNotifUtilsAPI,
- { keyserverID },
- devices,
- notification,
- );
+ return createWebNotification(encryptedNotifUtilsAPI, convertedData, devices);
}
-type WNSNotifInputData = {
- +notifTexts: ResolvedNotifTexts,
- +threadID: string,
- +keyserverID: string,
- +unreadCount: number,
- +platformDetails: PlatformDetails,
-};
-const wnsNotifInputDataValidator = tShape<WNSNotifInputData>({
- notifTexts: resolvedNotifTextsValidator,
- threadID: tID,
- keyserverID: t.String,
- unreadCount: t.Number,
- platformDetails: tPlatformDetails,
-});
async function prepareWNSNotification(
devices: $ReadOnlyArray<NotificationTargetDevice>,
inputData: WNSNotifInputData,
@@ -1433,37 +1208,7 @@
wnsNotifInputDataValidator,
inputData,
);
- const { notifTexts, threadID, unreadCount, keyserverID } = convertedData;
- const { merged, ...rest } = notifTexts;
- const notification = {
- ...rest,
- unreadCount,
- threadID,
- };
-
- if (
- encryptedNotifUtilsAPI.getNotifByteSize(JSON.stringify(notification)) >
- wnsMaxNotificationPayloadByteSize
- ) {
- console.warn('WNS notification exceeds size limit');
- }
-
- const shouldBeEncrypted = hasMinCodeVersion(inputData.platformDetails, {
- majorDesktop: 10,
- });
-
- if (!shouldBeEncrypted) {
- return devices.map(({ deviceToken }) => ({
- deviceToken,
- notification,
- }));
- }
- return await prepareEncryptedWNSNotifications(
- encryptedNotifUtilsAPI,
- { keyserverID },
- devices,
- notification,
- );
+ return createWNSNotification(encryptedNotifUtilsAPI, convertedData, devices);
}
type NotificationInfo =
diff --git a/keyserver/src/push/utils.js b/keyserver/src/push/utils.js
--- a/keyserver/src/push/utils.js
+++ b/keyserver/src/push/utils.js
@@ -8,6 +8,8 @@
import uuid from 'uuid';
import webpush from 'web-push';
+import { fcmMaxNotificationPayloadByteSize } from 'lib/push/android-notif-creators.js';
+import { wnsMaxNotificationPayloadByteSize } from 'lib/push/wns-notif-creators.js';
import type { PlatformDetails } from 'lib/types/device-types.js';
import type {
TargetedAndroidNotification,
@@ -34,14 +36,13 @@
'messaging/registration-token-not-registered',
'messaging/invalid-registration-token',
]);
-const fcmMaxNotificationPayloadByteSize = 4000;
+
const apnTokenInvalidationErrorCode = 410;
const apnBadRequestErrorCode = 400;
const apnBadTokenErrorString = 'BadDeviceToken';
const apnMaxNotificationPayloadByteSize = 4096;
const webInvalidTokenErrorCodes = [404, 410];
const wnsInvalidTokenErrorCodes = [404, 410];
-const wnsMaxNotificationPayloadByteSize = 5000;
export type APNPushResult =
| { +success: true }
diff --git a/lib/push/android-notif-creators.js b/lib/push/android-notif-creators.js
new file mode 100644
--- /dev/null
+++ b/lib/push/android-notif-creators.js
@@ -0,0 +1,254 @@
+// @flow
+
+import t, { type TInterface } from 'tcomb';
+
+import { prepareEncryptedAndroidVisualNotifications } from './crypto.js';
+import { hasMinCodeVersion } from '../shared/version-utils.js';
+import type { PlatformDetails } from '../types/device-types.js';
+import { messageTypes } from '../types/message-types-enum.js';
+import {
+ type RawMessageInfo,
+ rawMessageInfoValidator,
+} from '../types/message-types.js';
+import {
+ type AndroidVisualNotification,
+ type NotificationTargetDevice,
+ type TargetedAndroidNotification,
+ type ResolvedNotifTexts,
+ resolvedNotifTextsValidator,
+ type SenderDeviceDescriptor,
+ senderDeviceDescriptorValidator,
+ type EncryptedNotifUtilsAPI,
+} from '../types/notif-types.js';
+import { tID, tPlatformDetails, tShape } from '../utils/validation-utils.js';
+
+export const fcmMaxNotificationPayloadByteSize = 4000;
+
+type CommonNativeNotifInputData = $ReadOnly<{
+ +senderDeviceDescriptor: SenderDeviceDescriptor,
+ +notifTexts: ResolvedNotifTexts,
+ +newRawMessageInfos: RawMessageInfo[],
+ +threadID: string,
+ +collapseKey: ?string,
+ +badgeOnly: boolean,
+ +unreadCount?: number,
+ +platformDetails: PlatformDetails,
+}>;
+
+const commonNativeNotifInputDataValidator = tShape<CommonNativeNotifInputData>({
+ senderDeviceDescriptor: senderDeviceDescriptorValidator,
+ notifTexts: resolvedNotifTextsValidator,
+ newRawMessageInfos: t.list(rawMessageInfoValidator),
+ threadID: tID,
+ collapseKey: t.maybe(t.String),
+ badgeOnly: t.Boolean,
+ unreadCount: t.maybe(t.Number),
+ platformDetails: tPlatformDetails,
+});
+
+export type AndroidNotifInputData = {
+ ...CommonNativeNotifInputData,
+ +notifID: string,
+};
+
+export const androidNotifInputDataValidator: TInterface<AndroidNotifInputData> =
+ tShape<AndroidNotifInputData>({
+ ...commonNativeNotifInputDataValidator.meta.props,
+ notifID: t.String,
+ });
+
+async function createAndroidVisualNotification(
+ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+ inputData: AndroidNotifInputData,
+ devices: $ReadOnlyArray<NotificationTargetDevice>,
+): Promise<$ReadOnlyArray<TargetedAndroidNotification>> {
+ const {
+ senderDeviceDescriptor,
+ notifTexts,
+ newRawMessageInfos,
+ threadID,
+ collapseKey,
+ badgeOnly,
+ unreadCount,
+ platformDetails,
+ notifID,
+ } = inputData;
+
+ const canDecryptNonCollapsibleTextNotifs = hasMinCodeVersion(
+ platformDetails,
+ { native: 228 },
+ );
+ const isNonCollapsibleTextNotif =
+ newRawMessageInfos.every(
+ newRawMessageInfo => newRawMessageInfo.type === messageTypes.TEXT,
+ ) && !collapseKey;
+
+ const canDecryptAllNotifTypes = hasMinCodeVersion(platformDetails, {
+ native: 267,
+ });
+
+ const shouldBeEncrypted =
+ canDecryptAllNotifTypes ||
+ (canDecryptNonCollapsibleTextNotifs && isNonCollapsibleTextNotif);
+
+ const { merged, ...rest } = notifTexts;
+ const notification = {
+ data: {
+ ...rest,
+ threadID,
+ },
+ };
+
+ if (unreadCount) {
+ notification.data = {
+ ...notification.data,
+ badge: unreadCount.toString(),
+ };
+ }
+
+ let id;
+ if (collapseKey && canDecryptAllNotifTypes) {
+ id = notifID;
+ notification.data = {
+ ...notification.data,
+ collapseKey,
+ };
+ } else if (collapseKey) {
+ id = collapseKey;
+ } else {
+ id = notifID;
+ }
+
+ notification.data = {
+ ...notification.data,
+ id,
+ badgeOnly: badgeOnly ? '1' : '0',
+ };
+
+ const messageInfos = JSON.stringify(newRawMessageInfos);
+ const copyWithMessageInfos = {
+ ...notification,
+ data: { ...notification.data, messageInfos },
+ };
+
+ const priority = 'high';
+ if (!shouldBeEncrypted) {
+ const notificationToSend =
+ encryptedNotifUtilsAPI.getNotifByteSize(
+ JSON.stringify(copyWithMessageInfos),
+ ) <= fcmMaxNotificationPayloadByteSize
+ ? copyWithMessageInfos
+ : notification;
+
+ return devices.map(({ deviceToken }) => ({
+ priority,
+ notification: notificationToSend,
+ deviceToken,
+ }));
+ }
+
+ const notificationsSizeValidator = (notif: AndroidVisualNotification) => {
+ const serializedNotif = JSON.stringify(notif);
+ return (
+ !serializedNotif ||
+ encryptedNotifUtilsAPI.getNotifByteSize(serializedNotif) <=
+ fcmMaxNotificationPayloadByteSize
+ );
+ };
+
+ const notifsWithMessageInfos =
+ await prepareEncryptedAndroidVisualNotifications(
+ encryptedNotifUtilsAPI,
+ senderDeviceDescriptor,
+ devices,
+ copyWithMessageInfos,
+ notificationsSizeValidator,
+ );
+
+ const devicesWithExcessiveSizeNoHolders = notifsWithMessageInfos
+ .filter(({ payloadSizeExceeded }) => payloadSizeExceeded)
+ .map(({ cookieID, deviceToken }) => ({ cookieID, deviceToken }));
+
+ if (devicesWithExcessiveSizeNoHolders.length === 0) {
+ return notifsWithMessageInfos.map(
+ ({ notification: notif, deviceToken, encryptionOrder }) => ({
+ priority,
+ notification: notif,
+ deviceToken,
+ encryptionOrder,
+ }),
+ );
+ }
+
+ const canQueryBlobService = hasMinCodeVersion(platformDetails, {
+ native: 331,
+ });
+
+ let blobHash, blobHolders, encryptionKey, blobUploadError;
+ if (canQueryBlobService) {
+ ({ blobHash, blobHolders, encryptionKey, blobUploadError } =
+ await encryptedNotifUtilsAPI.uploadLargeNotifPayload(
+ JSON.stringify(copyWithMessageInfos.data),
+ devicesWithExcessiveSizeNoHolders.length,
+ ));
+ }
+
+ if (blobUploadError) {
+ console.warn(
+ `Failed to upload payload of notification: ${notifID} ` +
+ `due to error: ${blobUploadError}`,
+ );
+ }
+
+ let devicesWithExcessiveSize = devicesWithExcessiveSizeNoHolders;
+ if (
+ blobHash &&
+ encryptionKey &&
+ blobHolders &&
+ blobHolders.length === devicesWithExcessiveSizeNoHolders.length
+ ) {
+ notification.data = {
+ ...notification.data,
+ blobHash,
+ encryptionKey,
+ };
+
+ devicesWithExcessiveSize = blobHolders.map((holder, idx) => ({
+ ...devicesWithExcessiveSize[idx],
+ blobHolder: holder,
+ }));
+ }
+
+ const notifsWithoutMessageInfos =
+ await prepareEncryptedAndroidVisualNotifications(
+ encryptedNotifUtilsAPI,
+ senderDeviceDescriptor,
+ devicesWithExcessiveSize,
+ notification,
+ );
+
+ const targetedNotifsWithMessageInfos = notifsWithMessageInfos
+ .filter(({ payloadSizeExceeded }) => !payloadSizeExceeded)
+ .map(({ notification: notif, deviceToken, encryptionOrder }) => ({
+ priority,
+ notification: notif,
+ deviceToken,
+ encryptionOrder,
+ }));
+
+ const targetedNotifsWithoutMessageInfos = notifsWithoutMessageInfos.map(
+ ({ notification: notif, deviceToken, encryptionOrder }) => ({
+ priority,
+ notification: notif,
+ deviceToken,
+ encryptionOrder,
+ }),
+ );
+
+ return [
+ ...targetedNotifsWithMessageInfos,
+ ...targetedNotifsWithoutMessageInfos,
+ ];
+}
+
+export { createAndroidVisualNotification };
diff --git a/keyserver/src/push/crypto.js b/lib/push/crypto.js
copy from keyserver/src/push/crypto.js
copy to lib/push/crypto.js
--- a/keyserver/src/push/crypto.js
+++ b/lib/push/crypto.js
@@ -1,10 +1,5 @@
// @flow
-import apn from '@parse/node-apn';
-import crypto from 'crypto';
-import invariant from 'invariant';
-import _cloneDeep from 'lodash/fp/cloneDeep.js';
-
import type {
PlainTextWebNotification,
PlainTextWebNotificationPayload,
@@ -18,119 +13,7 @@
NotificationTargetDevice,
SenderDeviceDescriptor,
EncryptedNotifUtilsAPI,
-} from 'lib/types/notif-types.js';
-import { toBase64URL } from 'lib/utils/base64.js';
-
-import { encrypt, generateKey } from '../utils/aes-crypto-utils.js';
-import { getOlmUtility } from '../utils/olm-utils.js';
-
-async function encryptAPNsNotification(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- cookieID: string,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- notification: apn.Notification,
- codeVersion?: ?number,
- notificationSizeValidator?: apn.Notification => boolean,
- blobHolder?: ?string,
-): Promise<{
- +notification: apn.Notification,
- +payloadSizeExceeded: boolean,
- +encryptedPayloadHash?: string,
- +encryptionOrder?: number,
-}> {
- invariant(
- !notification.collapseId,
- `Collapse ID can't be directly stored in apn.Notification object due ` +
- `to security reasons. Please put it in payload property`,
- );
-
- const encryptedNotification = new apn.Notification();
-
- encryptedNotification.id = notification.id;
- encryptedNotification.payload.id = notification.id;
-
- if (blobHolder) {
- encryptedNotification.payload.blobHolder = blobHolder;
- }
-
- encryptedNotification.payload.keyserverID = notification.payload.keyserverID;
- encryptedNotification.topic = notification.topic;
- encryptedNotification.sound = notification.aps.sound;
- encryptedNotification.pushType = 'alert';
- encryptedNotification.mutableContent = true;
-
- const { id, ...payloadSansUnencryptedData } = notification.payload;
- const unencryptedPayload = {
- ...payloadSansUnencryptedData,
- badge: notification.aps.badge.toString(),
- merged: notification.body,
- };
-
- try {
- const unencryptedSerializedPayload = JSON.stringify(unencryptedPayload);
-
- let dbPersistCondition;
- if (notificationSizeValidator) {
- dbPersistCondition = (serializedPayload: string) => {
- const notifCopy = _cloneDeep(encryptedNotification);
- notifCopy.payload.encryptedPayload = serializedPayload;
- return notificationSizeValidator(notifCopy);
- };
- }
- const {
- encryptedData: serializedPayload,
- sizeLimitViolated: dbPersistConditionViolated,
- encryptionOrder,
- } = await encryptedNotifUtilsAPI.encryptSerializedNotifPayload(
- cookieID,
- unencryptedSerializedPayload,
- dbPersistCondition,
- );
-
- encryptedNotification.payload.encryptedPayload = serializedPayload.body;
- encryptedNotification.payload = {
- ...encryptedNotification.payload,
- ...senderDeviceDescriptor,
- };
-
- if (codeVersion && codeVersion >= 254 && codeVersion % 2 === 0) {
- encryptedNotification.aps = {
- alert: { body: 'ENCRYPTED' },
- ...encryptedNotification.aps,
- };
- }
-
- const encryptedPayloadHash = getOlmUtility().sha256(serializedPayload.body);
- return {
- notification: encryptedNotification,
- payloadSizeExceeded: !!dbPersistConditionViolated,
- encryptedPayloadHash,
- encryptionOrder,
- };
- } catch (e) {
- console.log('Notification encryption failed: ' + e);
-
- encryptedNotification.body = notification.body;
- encryptedNotification.threadId = notification.payload.threadID;
- invariant(
- typeof notification.aps.badge === 'number',
- 'Unencrypted notification must have badge as a number',
- );
- encryptedNotification.badge = notification.aps.badge;
-
- encryptedNotification.payload = {
- ...encryptedNotification.payload,
- ...notification.payload,
- encryptionFailed: 1,
- };
- return {
- notification: encryptedNotification,
- payloadSizeExceeded: notificationSizeValidator
- ? notificationSizeValidator(_cloneDeep(encryptedNotification))
- : false,
- };
- }
-}
+} from '../types/notif-types.js';
async function encryptAndroidNotificationPayload<T>(
encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
@@ -138,12 +21,21 @@
senderDeviceDescriptor: SenderDeviceDescriptor,
unencryptedPayload: T,
payloadSizeValidator?: (
- T | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
+ | T
+ | $ReadOnly<{
+ ...SenderDeviceDescriptor,
+ +encryptedPayload: string,
+ +type: '1' | '0',
+ }>,
) => boolean,
): Promise<{
+resultPayload:
| T
- | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
+ | $ReadOnly<{
+ ...SenderDeviceDescriptor,
+ +encryptedPayload: string,
+ +type: '1' | '0',
+ }>,
+payloadSizeExceeded: boolean,
+encryptionOrder?: number,
}> {
@@ -160,9 +52,10 @@
let dbPersistCondition;
if (payloadSizeValidator) {
- dbPersistCondition = (serializedPayload: string) =>
+ dbPersistCondition = (serializedPayload: string, type: '1' | '0') =>
payloadSizeValidator({
encryptedPayload: serializedPayload,
+ type,
...senderDeviceDescriptor,
});
}
@@ -179,8 +72,9 @@
return {
resultPayload: {
- encryptedPayload: serializedPayload.body,
...senderDeviceDescriptor,
+ encryptedPayload: serializedPayload.body,
+ type: serializedPayload.type ? '1' : '0',
},
payloadSizeExceeded: !!dbPersistConditionViolated,
encryptionOrder,
@@ -229,7 +123,11 @@
payloadSizeValidator = (
payload:
| AndroidVisualNotificationPayload
- | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
+ | $ReadOnly<{
+ ...SenderDeviceDescriptor,
+ +encryptedPayload: string,
+ +type: '0' | '1',
+ }>,
) => {
return notificationSizeValidator({
data: { ...unencryptedData, ...payload },
@@ -300,6 +198,7 @@
| $ReadOnly<{
...SenderDeviceDescriptor,
+encryptedPayload: string,
+ +type: '1' | '0',
+encryptionOrder?: number,
}>
| { ...T, +encryptionFailed: '1' },
@@ -320,6 +219,7 @@
return {
...senderDeviceDescriptor,
encryptedPayload: serializedPayload.body,
+ type: serializedPayload.type ? '1' : '0',
encryptionOrder,
};
} catch (e) {
@@ -371,68 +271,6 @@
};
}
-function prepareEncryptedAPNsNotifications(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- devices: $ReadOnlyArray<NotificationTargetDevice>,
- notification: apn.Notification,
- codeVersion?: ?number,
- notificationSizeValidator?: apn.Notification => boolean,
-): Promise<
- $ReadOnlyArray<{
- +cookieID: string,
- +deviceToken: string,
- +notification: apn.Notification,
- +payloadSizeExceeded: boolean,
- +encryptedPayloadHash?: string,
- +encryptionOrder?: number,
- }>,
-> {
- const notificationPromises = devices.map(
- async ({ cookieID, deviceToken, blobHolder }) => {
- const notif = await encryptAPNsNotification(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- notification,
- codeVersion,
- notificationSizeValidator,
- blobHolder,
- );
- return { cookieID, deviceToken, ...notif };
- },
- );
- return Promise.all(notificationPromises);
-}
-
-function prepareEncryptedIOSNotificationRescind(
- encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
- senderDeviceDescriptor: SenderDeviceDescriptor,
- devices: $ReadOnlyArray<NotificationTargetDevice>,
- notification: apn.Notification,
- codeVersion?: ?number,
-): Promise<
- $ReadOnlyArray<{
- +cookieID: string,
- +deviceToken: string,
- +notification: apn.Notification,
- }>,
-> {
- const notificationPromises = devices.map(
- async ({ deviceToken, cookieID }) => {
- const { notification: notif } = await encryptAPNsNotification(
- encryptedNotifUtilsAPI,
- cookieID,
- senderDeviceDescriptor,
- notification,
- codeVersion,
- );
- return { deviceToken, cookieID, notification: notif };
- },
- );
- return Promise.all(notificationPromises);
-}
-
function prepareEncryptedAndroidVisualNotifications(
encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
senderDeviceDescriptor: SenderDeviceDescriptor,
@@ -545,38 +383,9 @@
return Promise.all(notificationPromises);
}
-async function encryptBlobPayload(payload: string): Promise<{
- +encryptionKey: string,
- +encryptedPayload: Blob,
- +encryptedPayloadHash: string,
-}> {
- const encryptionKey = await generateKey();
- const encryptedPayload = await encrypt(
- encryptionKey,
- new TextEncoder().encode(payload),
- );
- const encryptedPayloadBuffer = Buffer.from(encryptedPayload);
- const blobHashBase64 = await crypto
- .createHash('sha256')
- .update(encryptedPayloadBuffer)
- .digest('base64');
- const blobHash = toBase64URL(blobHashBase64);
-
- const payloadBlob = new Blob([encryptedPayloadBuffer]);
- const encryptionKeyString = Buffer.from(encryptionKey).toString('base64');
- return {
- encryptionKey: encryptionKeyString,
- encryptedPayload: payloadBlob,
- encryptedPayloadHash: blobHash,
- };
-}
-
export {
- prepareEncryptedAPNsNotifications,
- prepareEncryptedIOSNotificationRescind,
prepareEncryptedAndroidVisualNotifications,
prepareEncryptedAndroidSilentNotifications,
prepareEncryptedWebNotifications,
prepareEncryptedWNSNotifications,
- encryptBlobPayload,
};
diff --git a/lib/push/web-notif-creators.js b/lib/push/web-notif-creators.js
new file mode 100644
--- /dev/null
+++ b/lib/push/web-notif-creators.js
@@ -0,0 +1,70 @@
+// @flow
+
+import t, { type TInterface } from 'tcomb';
+
+import { prepareEncryptedWebNotifications } from './crypto.js';
+import { hasMinCodeVersion } from '../shared/version-utils.js';
+import type { PlatformDetails } from '../types/device-types.js';
+import {
+ type NotificationTargetDevice,
+ type TargetedWebNotification,
+ type ResolvedNotifTexts,
+ resolvedNotifTextsValidator,
+ type SenderDeviceDescriptor,
+ senderDeviceDescriptorValidator,
+ type EncryptedNotifUtilsAPI,
+} from '../types/notif-types.js';
+import { tID, tPlatformDetails, tShape } from '../utils/validation-utils.js';
+
+export type WebNotifInputData = {
+ +id: string,
+ +notifTexts: ResolvedNotifTexts,
+ +threadID: string,
+ +senderDeviceDescriptor: SenderDeviceDescriptor,
+ +unreadCount: number,
+ +platformDetails: PlatformDetails,
+};
+
+export const webNotifInputDataValidator: TInterface<WebNotifInputData> =
+ tShape<WebNotifInputData>({
+ id: t.String,
+ notifTexts: resolvedNotifTextsValidator,
+ threadID: tID,
+ senderDeviceDescriptor: senderDeviceDescriptorValidator,
+ unreadCount: t.Number,
+ platformDetails: tPlatformDetails,
+ });
+
+async function createWebNotification(
+ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+ inputData: WebNotifInputData,
+ devices: $ReadOnlyArray<NotificationTargetDevice>,
+): Promise<$ReadOnlyArray<TargetedWebNotification>> {
+ const { id, notifTexts, threadID, unreadCount, senderDeviceDescriptor } =
+ inputData;
+
+ const { merged, ...rest } = notifTexts;
+ const notification = {
+ ...rest,
+ unreadCount,
+ id,
+ threadID,
+ };
+
+ const shouldBeEncrypted = hasMinCodeVersion(inputData.platformDetails, {
+ web: 43,
+ });
+
+ if (!shouldBeEncrypted) {
+ return devices.map(({ deviceToken }) => ({ deviceToken, notification }));
+ }
+
+ return prepareEncryptedWebNotifications(
+ encryptedNotifUtilsAPI,
+ senderDeviceDescriptor,
+ devices,
+ notification,
+ );
+}
+
+export { createWebNotification };
diff --git a/lib/push/wns-notif-creators.js b/lib/push/wns-notif-creators.js
new file mode 100644
--- /dev/null
+++ b/lib/push/wns-notif-creators.js
@@ -0,0 +1,77 @@
+// @flow
+
+import t, { type TInterface } from 'tcomb';
+
+import { prepareEncryptedWNSNotifications } from './crypto.js';
+import { hasMinCodeVersion } from '../shared/version-utils.js';
+import type { PlatformDetails } from '../types/device-types.js';
+import {
+ type NotificationTargetDevice,
+ type TargetedWNSNotification,
+ type ResolvedNotifTexts,
+ resolvedNotifTextsValidator,
+ type SenderDeviceDescriptor,
+ senderDeviceDescriptorValidator,
+ type EncryptedNotifUtilsAPI,
+} from '../types/notif-types.js';
+import { tID, tPlatformDetails, tShape } from '../utils/validation-utils.js';
+
+export const wnsMaxNotificationPayloadByteSize = 5000;
+
+export type WNSNotifInputData = {
+ +notifTexts: ResolvedNotifTexts,
+ +threadID: string,
+ +senderDeviceDescriptor: SenderDeviceDescriptor,
+ +unreadCount: number,
+ +platformDetails: PlatformDetails,
+};
+
+export const wnsNotifInputDataValidator: TInterface<WNSNotifInputData> =
+ tShape<WNSNotifInputData>({
+ notifTexts: resolvedNotifTextsValidator,
+ threadID: tID,
+ senderDeviceDescriptor: senderDeviceDescriptorValidator,
+ unreadCount: t.Number,
+ platformDetails: tPlatformDetails,
+ });
+
+async function createWNSNotification(
+ encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+ inputData: WNSNotifInputData,
+ devices: $ReadOnlyArray<NotificationTargetDevice>,
+): Promise<$ReadOnlyArray<TargetedWNSNotification>> {
+ const { notifTexts, threadID, unreadCount, senderDeviceDescriptor } =
+ inputData;
+ const { merged, ...rest } = notifTexts;
+ const notification = {
+ ...rest,
+ unreadCount,
+ threadID,
+ };
+
+ if (
+ encryptedNotifUtilsAPI.getNotifByteSize(JSON.stringify(notification)) >
+ wnsMaxNotificationPayloadByteSize
+ ) {
+ console.warn('WNS notification exceeds size limit');
+ }
+
+ const shouldBeEncrypted = hasMinCodeVersion(inputData.platformDetails, {
+ majorDesktop: 10,
+ });
+
+ if (!shouldBeEncrypted) {
+ return devices.map(({ deviceToken }) => ({
+ deviceToken,
+ notification,
+ }));
+ }
+ return await prepareEncryptedWNSNotifications(
+ encryptedNotifUtilsAPI,
+ senderDeviceDescriptor,
+ devices,
+ notification,
+ );
+}
+
+export { createWNSNotification };
diff --git a/lib/types/notif-types.js b/lib/types/notif-types.js
--- a/lib/types/notif-types.js
+++ b/lib/types/notif-types.js
@@ -1,7 +1,7 @@
// @flow
import type { EncryptResult } from '@commapp/olm';
-import t, { type TInterface } from 'tcomb';
+import t, { type TInterface, type TUnion } from 'tcomb';
import type { EntityText, ThreadEntity } from '../utils/entity-text.js';
import { tShape } from '../utils/validation-utils.js';
@@ -31,6 +31,12 @@
| { +keyserverID: string }
| { +senderDeviceID: string };
+export const senderDeviceDescriptorValidator: TUnion<SenderDeviceDescriptor> =
+ t.union([
+ tShape({ keyserverID: t.String }),
+ tShape({ senderDeviceID: t.String }),
+ ]);
+
export type PlainTextWebNotificationPayload = {
+body: string,
+prefix?: string,
@@ -49,7 +55,7 @@
...SenderDeviceDescriptor,
+id: string,
+encryptedPayload: string,
- +type?: '0' | '1',
+ +type: '0' | '1',
}>;
export type WebNotification =
@@ -68,7 +74,7 @@
export type EncryptedWNSNotification = $ReadOnly<{
...SenderDeviceDescriptor,
+encryptedPayload: string,
- +type?: '0' | '1',
+ +type: '0' | '1',
}>;
export type WNSNotification =
@@ -76,7 +82,7 @@
| EncryptedWNSNotification;
export type AndroidVisualNotificationPayloadBase = $ReadOnly<{
- +badge: string,
+ +badge?: string,
+body: string,
+title: string,
+prefix?: string,
@@ -104,13 +110,13 @@
type EncryptedThinThreadPayload = {
+keyserverID: string,
+encryptedPayload: string,
- +type?: '0' | '1',
+ +type: '0' | '1',
};
type EncryptedThickThreadPayload = {
+senderDeviceID: string,
+encryptedPayload: string,
- +type?: '0' | '1',
+ +type: '0' | '1',
};
export type AndroidVisualNotification = {
@@ -191,7 +197,10 @@
+encryptSerializedNotifPayload: (
cryptoID: string,
unencryptedPayload: string,
- encryptedPayloadSizeValidator?: (encryptedPayload: string) => boolean,
+ encryptedPayloadSizeValidator?: (
+ encryptedPayload: string,
+ type: '1' | '0',
+ ) => boolean,
) => Promise<{
+encryptedData: EncryptResult,
+sizeLimitViolated?: boolean,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Dec 8, 2:24 PM (7 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5848667
Default Alt Text
D12397.1765203845.diff (48 KB)
Attached To
Mode
D12397: Move notification creaction and encryption to lib except for APNs noifs.
Attached
Detach File
Event Timeline
Log In to Comment