Page MenuHomePhabricator

D13963.diff
No OneTemporary

D13963.diff

diff --git a/lib/shared/device-list-utils.js b/lib/shared/device-list-utils.js
--- a/lib/shared/device-list-utils.js
+++ b/lib/shared/device-list-utils.js
@@ -1,7 +1,17 @@
// @flow
import invariant from 'invariant';
+import * as React from 'react';
+import { IdentityClientContext } from './identity-client-context.js';
+import {
+ useBroadcastDeviceListUpdates,
+ useGetAndUpdateDeviceListsForUsers,
+} from '../hooks/peer-list-hooks.js';
+import {
+ getAllPeerDevices,
+ getForeignPeerDeviceIDs,
+} from '../selectors/user-selectors.js';
import type {
IdentityServiceClient,
RawDeviceList,
@@ -13,6 +23,7 @@
composeRawDeviceList,
rawDeviceListFromSignedList,
} from '../utils/device-list-utils.js';
+import { useSelector } from '../utils/redux-utils.js';
export type DeviceListVerificationResult =
| { +valid: true, +deviceList: RawDeviceList }
@@ -171,7 +182,7 @@
identityClient: IdentityServiceClient,
userID: string,
newDeviceID: string,
-) {
+): Promise<?SignedDeviceList> {
const { updateDeviceList } = identityClient;
invariant(
updateDeviceList,
@@ -182,12 +193,13 @@
const { devices } = await fetchLatestDeviceList(identityClient, userID);
if (devices.includes(newDeviceID)) {
// the device was already on the device list
- return;
+ return null;
}
const newDeviceList = composeRawDeviceList([...devices, newDeviceID]);
const signedDeviceList = await signDeviceListUpdate(newDeviceList);
await updateDeviceList(signedDeviceList);
+ return signedDeviceList;
}
async function removeDeviceFromDeviceList(
@@ -219,7 +231,7 @@
userID: string,
deviceIDToRemove: string,
newDeviceID: string,
-): Promise<void> {
+): Promise<?SignedDeviceList> {
const { updateDeviceList } = identityClient;
invariant(
updateDeviceList,
@@ -232,7 +244,7 @@
// If the device to remove is not on the list and the new device is already on
// the list, return
if (!devices.includes(deviceIDToRemove) && devices.includes(newDeviceID)) {
- return;
+ return null;
}
const newDevices = devices.filter(it => it !== deviceIDToRemove);
@@ -241,6 +253,118 @@
const newDeviceList = composeRawDeviceList(newDevices);
const signedDeviceList = await signDeviceListUpdate(newDeviceList);
await updateDeviceList(signedDeviceList);
+ return signedDeviceList;
+}
+
+type DeviceListUpdate =
+ | {
+ +type: 'add',
+ +deviceID: string,
+ }
+ | {
+ +type: 'replace',
+ +deviceIDToRemove: string,
+ +newDeviceID: string,
+ }
+ | {
+ +type: 'remove',
+ +deviceID: string,
+ };
+
+function useDeviceListUpdate(): (update: DeviceListUpdate) => Promise<void> {
+ const identityContext = React.useContext(IdentityClientContext);
+ invariant(identityContext, 'identity context not set');
+ const { identityClient, getAuthMetadata } = identityContext;
+
+ const allPeerDevices = useSelector(getAllPeerDevices);
+ const foreignPeerDevices = useSelector(getForeignPeerDeviceIDs);
+ const broadcastDeviceListUpdates = useBroadcastDeviceListUpdates();
+ const getAndUpdateDeviceListsForUsers = useGetAndUpdateDeviceListsForUsers();
+
+ const sendDeviceListUpdates = React.useCallback(
+ async (
+ signedDeviceList: ?SignedDeviceList,
+ userID: string,
+ primaryDeviceID: string,
+ ) => {
+ if (!signedDeviceList) {
+ return;
+ }
+
+ const deviceList = rawDeviceListFromSignedList(signedDeviceList);
+ const ownOtherDevices = deviceList.devices.filter(
+ it => it !== primaryDeviceID,
+ );
+
+ await Promise.all([
+ broadcastDeviceListUpdates(
+ [...ownOtherDevices, ...foreignPeerDevices],
+ signedDeviceList,
+ ),
+ // We need to call it in order to fetch platform details for
+ // the added device
+ getAndUpdateDeviceListsForUsers([userID]),
+ ]);
+ },
+ [
+ broadcastDeviceListUpdates,
+ foreignPeerDevices,
+ getAndUpdateDeviceListsForUsers,
+ ],
+ );
+
+ return React.useCallback(
+ async (update: DeviceListUpdate) => {
+ if (update.type === 'add') {
+ const { deviceID } = update;
+ const { userID, deviceID: primaryDeviceID } = await getAuthMetadata();
+ if (!userID || !primaryDeviceID) {
+ throw new Error('missing auth metadata');
+ }
+
+ const signedDeviceList = await addDeviceToDeviceList(
+ identityClient,
+ userID,
+ deviceID,
+ );
+ await sendDeviceListUpdates(signedDeviceList, userID, primaryDeviceID);
+ } else if (update.type === 'replace') {
+ const { deviceIDToRemove, newDeviceID } = update;
+ const { userID, deviceID: primaryDeviceID } = await getAuthMetadata();
+
+ if (!userID || !primaryDeviceID) {
+ throw new Error('missing auth metadata');
+ }
+
+ const signedDeviceList = await replaceDeviceInDeviceList(
+ identityClient,
+ userID,
+ deviceIDToRemove,
+ newDeviceID,
+ );
+ await sendDeviceListUpdates(signedDeviceList, userID, primaryDeviceID);
+ } else if (update.type === 'remove') {
+ const { deviceID } = update;
+ const { userID } = await getAuthMetadata();
+
+ if (!userID) {
+ throw new Error('missing auth metadata');
+ }
+
+ await removeDeviceFromDeviceList(identityClient, userID, deviceID);
+ await broadcastDeviceListUpdates(
+ allPeerDevices.filter(it => it !== deviceID),
+ );
+ }
+ },
+ [
+ allPeerDevices,
+ broadcastDeviceListUpdates,
+ getAuthMetadata,
+ identityClient,
+ sendDeviceListUpdates,
+ ],
+ );
}
export {
@@ -251,4 +375,5 @@
removeDeviceFromDeviceList,
replaceDeviceInDeviceList,
signDeviceListUpdate,
+ useDeviceListUpdate,
};
diff --git a/lib/tunnelbroker/use-peer-to-peer-message-handler.js b/lib/tunnelbroker/use-peer-to-peer-message-handler.js
--- a/lib/tunnelbroker/use-peer-to-peer-message-handler.js
+++ b/lib/tunnelbroker/use-peer-to-peer-message-handler.js
@@ -15,13 +15,10 @@
useBroadcastAccountDeletion,
useGetAndUpdateDeviceListsForUsers,
} from '../hooks/peer-list-hooks.js';
-import {
- getAllPeerDevices,
- getForeignPeerDeviceIDs,
-} from '../selectors/user-selectors.js';
+import { getForeignPeerDeviceIDs } from '../selectors/user-selectors.js';
import {
verifyAndGetDeviceList,
- removeDeviceFromDeviceList,
+ useDeviceListUpdate,
} from '../shared/device-list-utils.js';
import { dmOperationSpecificationTypes } from '../shared/dm-ops/dm-op-utils.js';
import { useProcessDMOperation } from '../shared/dm-ops/process-dm-ops.js';
@@ -71,16 +68,15 @@
const identityContext = React.useContext(IdentityClientContext);
invariant(identityContext, 'Identity context should be set');
- const { identityClient, getAuthMetadata } = identityContext;
- const broadcastDeviceListUpdates = useBroadcastDeviceListUpdates();
+ const { getAuthMetadata } = identityContext;
const reBroadcastAccountDeletion = useBroadcastAccountDeletion(
accountDeletionBroadcastOptions,
);
- const allPeerDevices = useSelector(getAllPeerDevices);
const dispatch = useDispatch();
const dispatchActionPromise = useDispatchActionPromise();
const primaryDeviceRequestedLogOut = useLogOut(primaryRequestLogoutOptions);
+ const runDeviceListUpdate = useDeviceListUpdate();
const processDMOperation = useProcessDMOperation();
@@ -112,15 +108,11 @@
userActionMessage.type ===
userActionsP2PMessageTypes.LOG_OUT_SECONDARY_DEVICE
) {
- const { userID, deviceID: deviceIDToLogOut } = senderInfo;
- await removeDeviceFromDeviceList(
- identityClient,
- userID,
- deviceIDToLogOut,
- );
- await broadcastDeviceListUpdates(
- allPeerDevices.filter(deviceID => deviceID !== deviceIDToLogOut),
- );
+ const { deviceID: deviceIDToLogOut } = senderInfo;
+ await runDeviceListUpdate({
+ type: 'remove',
+ deviceID: deviceIDToLogOut,
+ });
await sqliteAPI.removeInboundP2PMessages([messageID]);
} else if (
userActionMessage.type === userActionsP2PMessageTypes.DM_OPERATION
@@ -171,15 +163,13 @@
}
},
[
- allPeerDevices,
- broadcastDeviceListUpdates,
dispatch,
dispatchActionPromise,
getAuthMetadata,
- identityClient,
primaryDeviceRequestedLogOut,
processDMOperation,
reBroadcastAccountDeletion,
+ runDeviceListUpdate,
],
);
}
diff --git a/native/profile/linked-devices-bottom-sheet.react.js b/native/profile/linked-devices-bottom-sheet.react.js
--- a/native/profile/linked-devices-bottom-sheet.react.js
+++ b/native/profile/linked-devices-bottom-sheet.react.js
@@ -5,16 +5,13 @@
import { View, Text } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
-import { useBroadcastDeviceListUpdates } from 'lib/hooks/peer-list-hooks.js';
-import { getAllPeerDevices } from 'lib/selectors/user-selectors.js';
-import { removeDeviceFromDeviceList } from 'lib/shared/device-list-utils.js';
+import { useDeviceListUpdate } from 'lib/shared/device-list-utils.js';
import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
import { usePeerToPeerCommunication } from 'lib/tunnelbroker/peer-to-peer-context.js';
import {
userActionsP2PMessageTypes,
type DeviceLogoutP2PMessage,
} from 'lib/types/tunnelbroker/user-actions-peer-to-peer-message-types.js';
-import { useSelector } from 'lib/utils/redux-utils.js';
import { BottomSheetContext } from '../bottom-sheet/bottom-sheet-provider.react.js';
import BottomSheet from '../bottom-sheet/bottom-sheet.react.js';
@@ -47,11 +44,10 @@
const identityContext = React.useContext(IdentityClientContext);
invariant(identityContext, 'identity context not set');
- const { identityClient, getAuthMetadata } = identityContext;
+ const { getAuthMetadata } = identityContext;
- const broadcastDeviceListUpdates = useBroadcastDeviceListUpdates();
+ const runDeviceListUpdate = useDeviceListUpdate();
const { broadcastEphemeralMessage } = usePeerToPeerCommunication();
- const allPeerDevices = useSelector(getAllPeerDevices);
const bottomSheetContext = React.useContext(BottomSheetContext);
invariant(bottomSheetContext, 'bottomSheetContext should be set');
@@ -72,7 +68,10 @@
}
try {
- await removeDeviceFromDeviceList(identityClient, userID, deviceID);
+ await runDeviceListUpdate({
+ type: 'remove',
+ deviceID,
+ });
} catch (err) {
console.log('Primary device error:', err);
Alert.alert(
@@ -87,23 +86,17 @@
type: userActionsP2PMessageTypes.LOG_OUT_DEVICE,
};
- const sendLogoutMessagePromise = broadcastEphemeralMessage(
+ await broadcastEphemeralMessage(
JSON.stringify(messageContents),
[{ userID, deviceID }],
authMetadata,
);
- const broadcastUpdatePromise = broadcastDeviceListUpdates(
- allPeerDevices.filter(peerDeviceID => deviceID !== peerDeviceID),
- );
- await Promise.all([sendLogoutMessagePromise, broadcastUpdatePromise]);
bottomSheetRef.current?.close();
}, [
- broadcastDeviceListUpdates,
+ getAuthMetadata,
broadcastEphemeralMessage,
deviceID,
- allPeerDevices,
- getAuthMetadata,
- identityClient,
+ runDeviceListUpdate,
]);
const confirmDeviceRemoval = () => {
diff --git a/native/profile/secondary-device-qr-code-scanner.react.js b/native/profile/secondary-device-qr-code-scanner.react.js
--- a/native/profile/secondary-device-qr-code-scanner.react.js
+++ b/native/profile/secondary-device-qr-code-scanner.react.js
@@ -8,18 +8,10 @@
import { parseDataFromDeepLink } from 'lib/facts/links.js';
import {
- useBroadcastDeviceListUpdates,
- useGetAndUpdateDeviceListsForUsers,
-} from 'lib/hooks/peer-list-hooks.js';
-import {
- getForeignPeerDeviceIDs,
getOwnPeerDevices,
getKeyserverDeviceID,
} from 'lib/selectors/user-selectors.js';
-import {
- addDeviceToDeviceList,
- replaceDeviceInDeviceList,
-} from 'lib/shared/device-list-utils.js';
+import { useDeviceListUpdate } from 'lib/shared/device-list-utils.js';
import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js';
import {
@@ -40,7 +32,6 @@
type PeerToPeerMessage,
} from 'lib/types/tunnelbroker/peer-to-peer-message-types.js';
import { qrCodeAuthMessageTypes } from 'lib/types/tunnelbroker/qr-code-auth-message-types.js';
-import { rawDeviceListFromSignedList } from 'lib/utils/device-list-utils.js';
import { assertWithValidator } from 'lib/utils/validation-utils.js';
import type { ProfileNavigationProp } from './profile.react.js';
@@ -83,10 +74,8 @@
const secondaryDeviceID = React.useRef<?string>(null);
const secondaryDeviceType = React.useRef<?IdentityDeviceType>(null);
- const broadcastDeviceListUpdates = useBroadcastDeviceListUpdates();
- const getAndUpdateDeviceListsForUsers = useGetAndUpdateDeviceListsForUsers();
+ const runDeviceListUpdate = useDeviceListUpdate();
- const foreignPeerDevices = useSelector(getForeignPeerDeviceIDs);
const ownPeerDevices = useSelector(getOwnPeerDevices);
const keyserverDeviceID = getKeyserverDeviceID(ownPeerDevices);
const getBackupSecret = useGetBackupSecretForLoggedInUser();
@@ -130,30 +119,6 @@
return;
}
- invariant(identityContext, 'identity context not set');
- const { getAuthMetadata, identityClient } = identityContext;
- const { userID, deviceID } = await getAuthMetadata();
- if (!userID || !deviceID) {
- throw new Error('missing auth metadata');
- }
-
- const deviceLists =
- await identityClient.getDeviceListHistoryForUser(userID);
- invariant(deviceLists.length > 0, 'received empty device list history');
-
- const lastSignedDeviceList = deviceLists[deviceLists.length - 1];
- const deviceList = rawDeviceListFromSignedList(lastSignedDeviceList);
-
- const ownOtherDevices = deviceList.devices.filter(it => it !== deviceID);
-
- await Promise.all([
- broadcastDeviceListUpdates(
- [...ownOtherDevices, ...foreignPeerDevices],
- lastSignedDeviceList,
- ),
- getAndUpdateDeviceListsForUsers([userID]),
- ]);
-
if (!payload.requestBackupKeys) {
Alert.alert('Device added', 'Device registered successfully', [
{ text: 'OK', onPress: goBack },
@@ -190,16 +155,7 @@
{ text: 'OK', onPress: goBack },
]);
},
- [
- identityContext,
- broadcastDeviceListUpdates,
- foreignPeerDevices,
- getAndUpdateDeviceListsForUsers,
- getBackupSecret,
- tunnelbrokerContext,
- goBack,
- retrieveLatestBackupInfo,
- ],
+ [getBackupSecret, goBack, retrieveLatestBackupInfo, tunnelbrokerContext],
);
React.useEffect(() => {
@@ -259,12 +215,11 @@
if (!keyserverDeviceID) {
throw new Error('missing keyserver device ID');
}
- await replaceDeviceInDeviceList(
- identityContext.identityClient,
- userID,
- keyserverDeviceID,
- targetDeviceID,
- );
+ await runDeviceListUpdate({
+ type: 'replace',
+ deviceIDToRemove: keyserverDeviceID,
+ newDeviceID: targetDeviceID,
+ });
await sendDeviceListUpdateSuccessMessage();
} catch (err) {
console.log('Device replacement error:', err);
@@ -282,11 +237,10 @@
!keyserverDeviceID ||
keyserverDeviceID === targetDeviceID
) {
- await addDeviceToDeviceList(
- identityContext.identityClient,
- userID,
- targetDeviceID,
- );
+ await runDeviceListUpdate({
+ type: 'add',
+ deviceID: targetDeviceID,
+ });
await sendDeviceListUpdateSuccessMessage();
return;
}
@@ -314,7 +268,13 @@
]);
goBack();
}
- }, [goBack, identityContext, keyserverDeviceID, tunnelbrokerContext]);
+ }, [
+ goBack,
+ identityContext,
+ keyserverDeviceID,
+ runDeviceListUpdate,
+ tunnelbrokerContext,
+ ]);
const onPressSave = React.useCallback(async () => {
if (!urlInput) {

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 20, 10:43 PM (21 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2550562
Default Alt Text
D13963.diff (16 KB)

Event Timeline