Limit linked device interaction with backup service
This commit is contained in:
parent
4aecd47727
commit
3e24e510e6
5 changed files with 69 additions and 38 deletions
|
@ -40,7 +40,7 @@ import { isWindowDragElement } from './util/isWindowDragElement';
|
|||
import { assertDev, strictAssert } from './util/assert';
|
||||
import { filter } from './util/iterables';
|
||||
import { isNotNil } from './util/isNotNil';
|
||||
import { isBackupFeatureEnabled } from './util/isBackupEnabled';
|
||||
import { areRemoteBackupsTurnedOn } from './util/isBackupEnabled';
|
||||
import { setAppLoadingScreenMessage } from './setAppLoadingScreenMessage';
|
||||
import { IdleDetector } from './IdleDetector';
|
||||
import {
|
||||
|
@ -2012,7 +2012,7 @@ export async function startApp(): Promise<void> {
|
|||
drop(window.Signal.Services.initializeGroupCredentialFetcher());
|
||||
drop(AttachmentDownloadManager.start());
|
||||
|
||||
if (isBackupFeatureEnabled()) {
|
||||
if (areRemoteBackupsTurnedOn()) {
|
||||
backupsService.start();
|
||||
drop(AttachmentBackupManager.start());
|
||||
}
|
||||
|
|
|
@ -38,8 +38,13 @@ import {
|
|||
getBackupSignatureKey,
|
||||
getBackupMediaSignatureKey,
|
||||
} from './crypto';
|
||||
import { isTestOrMockEnvironment } from '../../environment';
|
||||
import {
|
||||
areRemoteBackupsTurnedOn,
|
||||
canAttemptRemoteBackupDownload,
|
||||
} from '../../util/isBackupEnabled';
|
||||
|
||||
const log = createLogger('credentials');
|
||||
const log = createLogger('Backup.Credentials');
|
||||
|
||||
const FETCH_INTERVAL = 3 * DAY;
|
||||
|
||||
|
@ -82,7 +87,7 @@ export class BackupCredentials {
|
|||
});
|
||||
|
||||
if (result === undefined) {
|
||||
log.info(`BackupCredentials: cache miss for ${now}`);
|
||||
log.info(`cache miss for ${now}`);
|
||||
credentials = await this.#fetch();
|
||||
result = credentials.find(({ type, redemptionTimeMs }) => {
|
||||
return type === credentialType && redemptionTimeMs === now;
|
||||
|
@ -112,7 +117,7 @@ export class BackupCredentials {
|
|||
return info;
|
||||
}
|
||||
|
||||
log.warn(`BackupCredentials: uploading signature key (${storageKey})`);
|
||||
log.warn(`uploading signature key (${storageKey})`);
|
||||
|
||||
const { server } = window.textsecure;
|
||||
strictAssert(server, 'server not available');
|
||||
|
@ -180,13 +185,13 @@ export class BackupCredentials {
|
|||
const nextFetchAt = lastFetchAt + FETCH_INTERVAL;
|
||||
const delay = Math.max(0, nextFetchAt - Date.now());
|
||||
|
||||
log.info(`BackupCredentials: scheduling fetch in ${delay}ms`);
|
||||
log.info(`scheduling fetch in ${delay}ms`);
|
||||
setTimeout(() => drop(this.#runPeriodicFetch()), delay);
|
||||
}
|
||||
|
||||
async #runPeriodicFetch(): Promise<void> {
|
||||
try {
|
||||
log.info('BackupCredentials: run periodic fetch');
|
||||
log.info('running periodic fetch');
|
||||
await this.#fetch();
|
||||
|
||||
const now = Date.now();
|
||||
|
@ -197,7 +202,7 @@ export class BackupCredentials {
|
|||
} catch (error) {
|
||||
const delay = this.#fetchBackoff.getAndIncrement();
|
||||
log.error(
|
||||
'BackupCredentials: periodic fetch failed with ' +
|
||||
'periodic fetch failed with ' +
|
||||
`error: ${toLogFormat(error)}, retrying in ${delay}ms`
|
||||
);
|
||||
setTimeout(() => this.#scheduleFetch(), delay);
|
||||
|
@ -220,7 +225,16 @@ export class BackupCredentials {
|
|||
}
|
||||
|
||||
async #doFetch(): Promise<ReadonlyArray<BackupCredentialWrapperType>> {
|
||||
log.info('BackupCredentials: fetching');
|
||||
const canInteractWithBackupService =
|
||||
areRemoteBackupsTurnedOn() || canAttemptRemoteBackupDownload();
|
||||
|
||||
if (!canInteractWithBackupService) {
|
||||
throw new Error(
|
||||
'Cannot fetch credentials; remote backups are not active'
|
||||
);
|
||||
}
|
||||
|
||||
log.info('fetching');
|
||||
|
||||
const now = Date.now();
|
||||
const startDayInMs = toDayMillis(now);
|
||||
|
@ -239,37 +253,39 @@ export class BackupCredentials {
|
|||
endDayInMs,
|
||||
});
|
||||
} catch (error) {
|
||||
if (!(error instanceof HTTPError)) {
|
||||
// A 404 indicates the backupId has not been set; only primary devices can set the
|
||||
// backupId
|
||||
if (
|
||||
(isTestOrMockEnvironment() ||
|
||||
window.ConversationController.areWePrimaryDevice()) &&
|
||||
error instanceof HTTPError &&
|
||||
error.code === 404
|
||||
) {
|
||||
// Backup id is missing
|
||||
const messagesRequest = messagesCtx.getRequest();
|
||||
const mediaRequest = mediaCtx.getRequest();
|
||||
|
||||
// Set it
|
||||
await server.setBackupId({
|
||||
messagesBackupAuthCredentialRequest: messagesRequest.serialize(),
|
||||
mediaBackupAuthCredentialRequest: mediaRequest.serialize(),
|
||||
});
|
||||
|
||||
// And try again!
|
||||
response = await server.getBackupCredentials({
|
||||
startDayInMs,
|
||||
endDayInMs,
|
||||
});
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (error.code !== 404) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Backup id is missing
|
||||
const messagesRequest = messagesCtx.getRequest();
|
||||
const mediaRequest = mediaCtx.getRequest();
|
||||
|
||||
// Set it
|
||||
await server.setBackupId({
|
||||
messagesBackupAuthCredentialRequest: messagesRequest.serialize(),
|
||||
mediaBackupAuthCredentialRequest: mediaRequest.serialize(),
|
||||
});
|
||||
|
||||
// And try again!
|
||||
response = await server.getBackupCredentials({
|
||||
startDayInMs,
|
||||
endDayInMs,
|
||||
});
|
||||
}
|
||||
|
||||
const { messages: messageCredentials, media: mediaCredentials } =
|
||||
response.credentials;
|
||||
|
||||
log.info(
|
||||
'BackupCredentials: got ' +
|
||||
`${messageCredentials.length}/${mediaCredentials.length}`
|
||||
`got ${messageCredentials.length}/${mediaCredentials.length} message/media credentials`
|
||||
);
|
||||
|
||||
const serverPublicParams = new GenericServerPublicParams(
|
||||
|
@ -352,7 +368,7 @@ export class BackupCredentials {
|
|||
|
||||
const startMs = result[0].redemptionTimeMs;
|
||||
const endMs = result[result.length - 1].redemptionTimeMs;
|
||||
log.info(`BackupCredentials: saved [${startMs}, ${endMs}]`);
|
||||
log.info(`saved [${startMs}, ${endMs}]`);
|
||||
|
||||
strictAssert(result.length === 14, 'Expected one week of credentials');
|
||||
|
||||
|
|
|
@ -84,8 +84,9 @@ import {
|
|||
} from './util/localBackup';
|
||||
import { AttachmentLocalBackupManager } from '../../jobs/AttachmentLocalBackupManager';
|
||||
import { decipherWithAesKey } from '../../util/decipherWithAesKey';
|
||||
import { areRemoteBackupsTurnedOn } from '../../util/isBackupEnabled';
|
||||
|
||||
const log = createLogger('index');
|
||||
const log = createLogger('backupsService');
|
||||
|
||||
export { BackupType };
|
||||
|
||||
|
@ -162,13 +163,18 @@ export class BackupsService {
|
|||
);
|
||||
|
||||
public start(): void {
|
||||
if (!areRemoteBackupsTurnedOn()) {
|
||||
log.warn('remote backups are not turned on; not starting');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.#isStarted) {
|
||||
log.warn('BackupsService: already started');
|
||||
log.warn('already started');
|
||||
return;
|
||||
}
|
||||
|
||||
this.#isStarted = true;
|
||||
log.info('BackupsService: starting...');
|
||||
log.info('starting...');
|
||||
|
||||
setInterval(() => {
|
||||
drop(this.#runPeriodicRefresh());
|
||||
|
|
|
@ -64,9 +64,9 @@ import { SignalService as Proto } from '../protobuf';
|
|||
import { createLogger } from '../logging/log';
|
||||
import type { StorageAccessType } from '../types/Storage';
|
||||
import { getRelativePath, createName } from '../util/attachmentPath';
|
||||
import { isBackupFeatureEnabled } from '../util/isBackupEnabled';
|
||||
import { isLinkAndSyncEnabled } from '../util/isLinkAndSyncEnabled';
|
||||
import { getMessageQueueTime } from '../util/getMessageQueueTime';
|
||||
import { canAttemptRemoteBackupDownload } from '../util/isBackupEnabled';
|
||||
|
||||
const log = createLogger('AccountManager');
|
||||
|
||||
|
@ -1120,7 +1120,7 @@ export default class AccountManager extends EventTarget {
|
|||
}
|
||||
|
||||
const shouldDownloadBackup =
|
||||
isBackupFeatureEnabled() ||
|
||||
canAttemptRemoteBackupDownload() ||
|
||||
(isLinkAndSyncEnabled() && options.ephemeralBackupKey);
|
||||
|
||||
// Set backup download path before storing credentials to ensure that
|
||||
|
|
|
@ -5,6 +5,15 @@ import * as RemoteConfig from '../RemoteConfig';
|
|||
import { isTestOrMockEnvironment } from '../environment';
|
||||
import { isStagingServer } from './isStagingServer';
|
||||
|
||||
export function areRemoteBackupsTurnedOn(): boolean {
|
||||
return isBackupFeatureEnabled() && window.storage.get('backupTier') != null;
|
||||
}
|
||||
|
||||
// Downloading from a remote backup is currently a test-only feature
|
||||
export function canAttemptRemoteBackupDownload(): boolean {
|
||||
return isBackupFeatureEnabled() && isTestOrMockEnvironment();
|
||||
}
|
||||
|
||||
export function isBackupFeatureEnabled(): boolean {
|
||||
if (isStagingServer() || isTestOrMockEnvironment()) {
|
||||
return true;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue