From 7a6ea97efbed4301b299800bd5cd1d634d0f8424 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Mon, 15 Jun 2020 09:20:00 +1000 Subject: [PATCH] Handle strings in MultiDeviceProtocol --- js/background.js | 15 +- js/models/conversations.js | 9 +- js/modules/loki_file_server_api.js | 3 +- libtextsecure/account_manager.js | 3 +- libtextsecure/message_receiver.js | 3 +- ts/session/protocols/MultiDeviceProtocol.ts | 159 +++++++++++--------- ts/session/protocols/index.ts | 4 +- tslint.json | 2 +- 8 files changed, 108 insertions(+), 90 deletions(-) diff --git a/js/background.js b/js/background.js index fc68416ea..cada04a5f 100644 --- a/js/background.js +++ b/js/background.js @@ -1426,10 +1426,8 @@ Whisper.events.on('devicePairingRequestRejected', async pubKey => { await libloki.storage.removeContactPreKeyBundle(pubKey); - - const pubKeyObject = new libsession.Types.PubKey(pubKey); await libsession.Protocols.MultiDeviceProtocol.removePairingAuthorisations( - pubKeyObject + pubKey ); }); @@ -1438,9 +1436,8 @@ if (isSecondaryDevice) { return; } - const pubKeyObject = new libsession.Types.PubKey(pubKey); await libsession.Protocols.MultiDeviceProtocol.removePairingAuthorisations( - pubKeyObject + pubKey ); await window.lokiFileServerAPI.updateOurDeviceMapping(); // TODO: we should ensure the message was sent and retry automatically if not @@ -1755,6 +1752,8 @@ return; } + // A sender here could be referring to a group. + // Groups don't have primary devices so we need to take that into consideration. const user = libsession.Types.PubKey.from(sender); const primaryDevice = user ? await libsession.Protocols.MultiDeviceProtocol.getPrimaryDevice(user) @@ -1818,18 +1817,16 @@ } const ourPrimaryKey = window.storage.get('primaryDevicePubKey'); if (ourPrimaryKey) { - const user = new libsession.Types.PubKey(ourPrimaryKey); const secondaryDevices = await libsession.Protocols.MultiDeviceProtocol.getSecondaryDevices( - user + ourPrimaryKey ); if (secondaryDevices.some(device => device.key === id)) { await conversation.setSecondaryStatus(true, ourPrimaryKey); } } - const user = new libsession.Types.PubKey(id); const devices = await libsession.Protocols.MultiDeviceProtocol.getAllDevices( - user + id ); const deviceConversations = await Promise.all( devices.map(d => diff --git a/js/models/conversations.js b/js/models/conversations.js index d7089c87d..48997b89c 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -879,7 +879,14 @@ const device = window.libsession.Types.PubKey.from(this.id); if (device) { - return ConversationController.getOrCreateAndWait(device.key, 'private'); + const primary = await window.libsession.Protocols.MultiDeviceProtocol.getPrimaryDevice( + device + ); + + return ConversationController.getOrCreateAndWait( + primary.key, + 'private' + ); } // Something funky has happened diff --git a/js/modules/loki_file_server_api.js b/js/modules/loki_file_server_api.js index a89452911..b6f87b175 100644 --- a/js/modules/loki_file_server_api.js +++ b/js/modules/loki_file_server_api.js @@ -219,9 +219,8 @@ class LokiHomeServerInstance extends LokiFileServerInstance { async updateOurDeviceMapping() { const isPrimary = !storage.get('isSecondaryDevice'); - const ourKey = new window.libsession.Types.PubKey(this.ourKey); const authorisations = await window.libsession.Protocols.MultiDeviceProtocol.getPairingAuthorisations( - ourKey + this.ourKey ); return this._setOurDeviceMapping(authorisations, isPrimary); diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index 5385ccd5b..563a4baa5 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -605,9 +605,8 @@ e && e.stack ? e.stack : e ); // File server upload failed or message sending failed, we should rollback changes - const pubKey = new libsession.Types.PubKey(secondaryDevicePubKey); await libsession.Protocols.MultiDeviceProtocol.removePairingAuthorisations( - pubKey + secondaryDevicePubKey ); await lokiFileServerAPI.updateOurDeviceMapping(); throw e; diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index 6dfd31aa8..7c00b7b8d 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -1720,9 +1720,8 @@ MessageReceiver.prototype.extend({ async handleSyncMessage(envelope, syncMessage) { // We should only accept sync messages from our devices const ourNumber = textsecure.storage.user.getNumber(); - const user = new libsession.Types.PubKey(ourNumber); const ourDevices = await libsession.Protocols.MultiDeviceProtocol.getAllDevices( - user + ourNumber ); const validSyncSender = ourDevices.some( device => device.key === envelope.source diff --git a/ts/session/protocols/MultiDeviceProtocol.ts b/ts/session/protocols/MultiDeviceProtocol.ts index 7428b1dba..737ea13b0 100644 --- a/ts/session/protocols/MultiDeviceProtocol.ts +++ b/ts/session/protocols/MultiDeviceProtocol.ts @@ -9,85 +9,102 @@ import { PrimaryPubKey, PubKey, SecondaryPubKey } from '../types'; // TODO: We should fetch mappings when we can and only fetch them once every 5 minutes or something -/** - * Save pairing authorisation to the database. - * @param authorisation The pairing authorisation. - */ -export async function savePairingAuthorisation( - authorisation: PairingAuthorisation -): Promise { - return createOrUpdatePairingAuthorisation(authorisation); -} +/* + The reason we're exporing a class here instead of just exporting the functions directly is for the sake of testing. + We might want to stub out specific functions inside the multi device protocol itself but when export functions directly then it's not possible without weird hacks. +*/ +// tslint:disable-next-line: no-unnecessary-class +export class MultiDeviceProtocol { + /** + * Save pairing authorisation to the database. + * @param authorisation The pairing authorisation. + */ + public static async savePairingAuthorisation( + authorisation: PairingAuthorisation + ): Promise { + return createOrUpdatePairingAuthorisation(authorisation); + } -/** - * Get pairing authorisations for a given device. - * @param device The device to get pairing authorisations for. - */ -export async function getPairingAuthorisations( - device: PubKey -): Promise> { - return getPairingAuthorisationsFor(device.key); -} + /** + * Get pairing authorisations for a given device. + * @param device The device to get pairing authorisations for. + */ + public static async getPairingAuthorisations( + device: PubKey | string + ): Promise> { + const pubKey = typeof device === 'string' ? new PubKey(device) : device; -/** - * Remove all pairing authorisations for a given device. - * @param device The device to remove authorisation for. - */ -export async function removePairingAuthorisations( - device: PubKey -): Promise { - return removePairingAuthorisationsFor(device.key); -} - -/** - * Get all devices linked to a user. - * - * @param user The user to get all the devices from. - */ -export async function getAllDevices(user: PubKey): Promise> { - const authorisations = await getPairingAuthorisations(user); - const devices = _.flatMap( - authorisations, - ({ primaryDevicePubKey, secondaryDevicePubKey }) => [ - primaryDevicePubKey, - secondaryDevicePubKey, - ] - ); + return getPairingAuthorisationsFor(pubKey.key); + } - return [...new Set(devices)].map(p => new PubKey(p)); -} + /** + * Remove all pairing authorisations for a given device. + * @param device The device to remove authorisation for. + */ + public static async removePairingAuthorisations( + device: PubKey | string + ): Promise { + const pubKey = typeof device === 'string' ? new PubKey(device) : device; -/** - * Get the primary device linked to a user. - * - * @param user The user to get primary device for. - */ -export async function getPrimaryDevice(user: PubKey): Promise { - const authorisations = await getPairingAuthorisations(user); - if (authorisations.length === 0) { - return user; + return removePairingAuthorisationsFor(pubKey.key); } - const pubKey = PrimaryPubKey.from(authorisations[0].primaryDevicePubKey); - if (!pubKey) { - throw new Error(`Primary user public key is invalid for ${user.key}.`); + /** + * Get all devices linked to a user. + * + * @param user The user to get all the devices from. + */ + public static async getAllDevices( + user: PubKey | string + ): Promise> { + const pubKey = typeof user === 'string' ? new PubKey(user) : user; + const authorisations = await this.getPairingAuthorisations(pubKey); + const devices = _.flatMap( + authorisations, + ({ primaryDevicePubKey, secondaryDevicePubKey }) => [ + primaryDevicePubKey, + secondaryDevicePubKey, + ] + ); + + return [...new Set(devices)].map(p => new PubKey(p)); } - return pubKey; -} + /** + * Get the primary device linked to a user. + * + * @param user The user to get primary device for. + */ + public static async getPrimaryDevice( + user: PubKey | string + ): Promise { + const pubKey = typeof user === 'string' ? new PubKey(user) : user; + const authorisations = await this.getPairingAuthorisations(pubKey); + if (authorisations.length === 0) { + return pubKey; + } + + const primary = PrimaryPubKey.from(authorisations[0].primaryDevicePubKey); + if (!primary) { + throw new Error(`Primary user public key is invalid for ${pubKey.key}.`); + } -/** - * Get all the secondary devices linked to a user. - * - * @param user The user to get the devices from. - */ -export async function getSecondaryDevices( - user: PubKey -): Promise> { - const primary = await getPrimaryDevice(user); - const authorisations = await getPairingAuthorisations(primary); + return primary; + } + + /** + * Get all the secondary devices linked to a user. + * + * @param user The user to get the devices from. + */ + public static async getSecondaryDevices( + user: PubKey | string + ): Promise> { + const primary = await this.getPrimaryDevice(user); + const authorisations = await this.getPairingAuthorisations(primary); - return authorisations - .map(a => a.secondaryDevicePubKey) - .map(pubKey => new SecondaryPubKey(pubKey)); + return authorisations + .map(a => a.secondaryDevicePubKey) + .map(pubKey => new SecondaryPubKey(pubKey)); + } } diff --git a/ts/session/protocols/index.ts b/ts/session/protocols/index.ts index 38fe021c0..375ad9391 100644 --- a/ts/session/protocols/index.ts +++ b/ts/session/protocols/index.ts @@ -1,4 +1,4 @@ import { SessionProtocol } from './SessionProtocol'; -import * as MultiDeviceProtocol from './MultiDeviceProtocol'; +export * from './MultiDeviceProtocol'; -export { SessionProtocol, MultiDeviceProtocol }; +export { SessionProtocol }; diff --git a/tslint.json b/tslint.json index f9428ed5e..628e59a93 100644 --- a/tslint.json +++ b/tslint.json @@ -92,7 +92,7 @@ "function-name": [ true, { - "function-regex": "^[a-z][\\w\\d]+$", + "function-regex": "^_?[a-z][\\w\\d]+$", "method-regex": "^[a-z][\\w\\d]+$", "private-method-regex": "^[a-z][\\w\\d]+$", "protected-method-regex": "^[a-z][\\w\\d]+$",