Page MenuHomePhorge

D12397.1765203845.diff
No OneTemporary

Size
48 KB
Referenced Files
None
Subscribers
None

D12397.1765203845.diff

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

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)

Event Timeline