Fetch device mappings from server when needed

pull/1176/head
Mikunj 5 years ago
parent 7a6ea97efb
commit 7942ba086f

@ -0,0 +1,13 @@
interface DeviceMappingAnnotation {
isPrimary: boolean;
authorisations: Array<{
primaryDevicePubKey: string;
secondaryDevicePubKey: string;
requestSignature: string; // base64
grantSignature: string; // base64
}>;
}
interface LokiFileServerInstance {
getUserDeviceMapping(pubKey: string): Promise<DeviceMappingAnnotation>;
}

@ -6,8 +6,8 @@ import {
removePairingAuthorisationsFor,
} from '../../../js/modules/data';
import { PrimaryPubKey, PubKey, SecondaryPubKey } from '../types';
// TODO: We should fetch mappings when we can and only fetch them once every 5 minutes or something
import { UserUtil } from '../../util';
import { lokiFileServerAPI } from '../../window';
/*
The reason we're exporing a class here instead of just exporting the functions directly is for the sake of testing.
@ -15,6 +15,88 @@ import { PrimaryPubKey, PubKey, SecondaryPubKey } from '../types';
*/
// tslint:disable-next-line: no-unnecessary-class
export class MultiDeviceProtocol {
public static refreshDelay: number = 5 * 1000 * 1000; // 5 minutes
private static lastFetch: { [device: string]: number } = {};
/**
* Fetch pairing authorisations from the file server if needed.
* This shouldn't be called outside of the MultiDeviceProtocol file, it is public so it can be stubbed in tests.
*
* This will fetch authorisations if:
* - It is not one of our device
* - The time since last fetch is more than refresh delay
*/
public static async _fetchPairingAuthorisationsIfNeeded(
device: PubKey
): Promise<void> {
// This return here stops an infinite loop when we get all our other devices
const ourKey = await UserUtil.getCurrentDevicePubKey();
if (!ourKey || device.key === ourKey) {
return;
}
// We always prefer our local pairing over the one on the server
const ourDevices = await this.getAllDevices(ourKey);
if (ourDevices.some(d => d.key === device.key)) {
return;
}
// Only fetch if we hit the refresh delay
const lastFetchTime = this.lastFetch[device.key];
if (lastFetchTime && lastFetchTime + this.refreshDelay < Date.now()) {
return;
}
this.lastFetch[device.key] = Date.now();
try {
const authorisations = await this.fetchPairingAuthorisations(device);
// TODO: validate?
await Promise.all(authorisations.map(this.savePairingAuthorisation));
} catch (e) {
// Something went wrong, let it re-try another time
this.lastFetch[device.key] = lastFetchTime;
}
}
/**
* This function shouldn't be called outside of tests!!
*/
public static _resetFetchCache() {
this.lastFetch = {};
}
/**
* Fetch pairing authorisations for the given device from the file server.
* This function will not save the authorisations to the database.
*
* @param device The device to fetch the authorisation for.
*/
public static async fetchPairingAuthorisations(
device: PubKey
): Promise<Array<PairingAuthorisation>> {
if (!lokiFileServerAPI) {
throw new Error('lokiFileServerAPI is not initialised.');
}
const mapping = await lokiFileServerAPI.getUserDeviceMapping(device.key);
// TODO: Filter out invalid authorisations
return mapping.authorisations.map(
({
primaryDevicePubKey,
secondaryDevicePubKey,
requestSignature,
grantSignature,
}) => ({
primaryDevicePubKey,
secondaryDevicePubKey,
requestSignature: Buffer.from(requestSignature, 'base64').buffer,
grantSignature: Buffer.from(grantSignature, 'base64').buffer,
})
);
}
/**
* Save pairing authorisation to the database.
* @param authorisation The pairing authorisation.
@ -33,6 +115,7 @@ export class MultiDeviceProtocol {
device: PubKey | string
): Promise<Array<PairingAuthorisation>> {
const pubKey = typeof device === 'string' ? new PubKey(device) : device;
await this._fetchPairingAuthorisationsIfNeeded(pubKey);
return getPairingAuthorisationsFor(pubKey.key);
}

@ -76,6 +76,7 @@ interface WindowInterface extends Window {
lokiMessageAPI: LokiMessageAPI;
lokiPublicChatAPI: LokiPublicChatFactoryAPI;
lokiFileServerAPI: LokiFileServerInstance;
}
// In the case for tests
@ -140,3 +141,4 @@ export const textsecure = window.textsecure;
export const lokiMessageAPI = window.lokiMessageAPI;
export const lokiPublicChatAPI = window.lokiPublicChatAPI;
export const lokiFileServerAPI = window.lokiFileServerAPI;

@ -93,10 +93,10 @@
true,
{
"function-regex": "^_?[a-z][\\w\\d]+$",
"method-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]+$",
"static-method-regex": "^[a-zA-Z][\\w\\d]+$"
"static-method-regex": "^_?[a-zA-Z][\\w\\d]+$"
}
],

Loading…
Cancel
Save