diff --git a/native/backup/backup-handler.js b/native/backup/backup-handler.js --- a/native/backup/backup-handler.js +++ b/native/backup/backup-handler.js @@ -9,11 +9,14 @@ import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js'; import { useClientBackup } from './use-client-backup.js'; +import { useGetBackupSecretForLoggedInUser } from './use-get-backup-secret.js'; import { commCoreModule } from '../native-modules.js'; import { useSelector } from '../redux/redux-utils.js'; import Alert from '../utils/alert.js'; import { useStaffCanSee } from '../utils/staff-utils.js'; +const millisecondsPerDay = 24 * 60 * 60 * 1000; + function BackupHandler(): null { const loggedIn = useSelector(isLoggedIn); const staffCanSee = useStaffCanSee(); @@ -26,7 +29,9 @@ state => state.backupStore.latestBackupInfo, ); const dispatchActionPromise = useDispatchActionPromise(); - const { createUserKeysBackup } = useClientBackup(); + const { createUserKeysBackup, retrieveLatestBackupInfo, getBackupUserKeys } = + useClientBackup(); + const getBackupSecret = useGetBackupSecretForLoggedInUser(); const backupUploadInProgress = React.useRef(false); const [handlerStarted, setHandlerStarted] = React.useState(false); @@ -73,13 +78,58 @@ handlerStarted, ]); + const testUserKeysRestore = React.useCallback(async () => { + if (!latestBackupInfo?.backupID) { + return; + } + const { + latestBackupInfo: { backupID }, + userIdentifier, + } = await retrieveLatestBackupInfo(); + + const backupSecret = await getBackupSecret(); + const { + backupDataKey: backupDataKeyFromBackup, + backupLogDataKey: backupLogDataKeyFromBackup, + } = await getBackupUserKeys(userIdentifier, backupSecret, backupID); + + const { + backupID: localBackupID, + backupDataKey: localBackupDataKey, + backupLogDataKey: localBackupLogDataKey, + } = await commCoreModule.getQRAuthBackupData(); + + const backupIDCheck = + latestBackupInfo.backupID === backupID && backupID === localBackupID; + const keysCheck = + backupDataKeyFromBackup === localBackupDataKey && + backupLogDataKeyFromBackup === localBackupLogDataKey; + + if (!backupIDCheck || !keysCheck) { + throw new Error( + '\n' + + `backupID: ${backupID}\n` + + `latestBackupInfo.backupID: ${latestBackupInfo.backupID}\n` + + `localBackupID: ${localBackupID}\n` + + `backupDataKeyFromBackup: ${backupDataKeyFromBackup}\n` + + `backupLogDataKeyFromBackup: ${backupLogDataKeyFromBackup}\n` + + `localBackupDataKey: ${localBackupDataKey}\n` + + `localBackupLogDataKey: ${localBackupLogDataKey}\n`, + ); + } + }, [ + getBackupSecret, + getBackupUserKeys, + latestBackupInfo?.backupID, + retrieveLatestBackupInfo, + ]); + React.useEffect(() => { if ( !staffCanSee || !canPerformBackupOperation || !handlerStarted || - backupUploadInProgress.current || - !!latestBackupInfo + backupUploadInProgress.current ) { return; } @@ -92,6 +142,22 @@ return; } + if (latestBackupInfo) { + const timestamp = latestBackupInfo.timestamp; + // If last upload one less than 24h ago ignore it + if (timestamp >= Date.now() - millisecondsPerDay) { + return; + } + + try { + await testUserKeysRestore(); + } catch (err) { + const message = getMessageForException(err) ?? 'unknown error'; + Alert.alert('Error restoring User Keys backup', message); + console.log('Error restoring User Keys backup:', message); + } + } + try { const promise = createUserKeysBackup(); void dispatchActionPromise(createUserKeysBackupActionTypes, promise); @@ -111,6 +177,7 @@ handlerStarted, latestBackupInfo, staffCanSee, + testUserKeysRestore, ]); return null;