Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3320699
D13963.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Referenced Files
None
Subscribers
None
D13963.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D13963: [lib] create hook to run Device List Update Protocol
Attached
Detach File
Event Timeline
Log In to Comment