Block WebAPI during active registration

This commit is contained in:
Fedor Indutny 2021-12-17 22:26:50 +01:00 committed by GitHub
parent 9e9e5274cf
commit 8070b8b14f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 35 deletions

View File

@ -172,24 +172,32 @@ export default class AccountManager extends EventTarget {
const profileKey = getRandomBytes(PROFILE_KEY_LENGTH);
const accessKey = deriveAccessKey(profileKey);
await this.createAccount({
number,
verificationCode,
identityKeyPair,
pniKeyPair,
profileKey,
accessKey,
});
const registrationBaton = this.server.startRegistration();
try {
await this.createAccount({
number,
verificationCode,
identityKeyPair,
pniKeyPair,
profileKey,
accessKey,
});
await this.clearSessionsAndPreKeys();
await this.clearSessionsAndPreKeys();
await Promise.all(
[UUIDKind.ACI, UUIDKind.PNI].map(async kind => {
const keys = await this.generateKeys(SIGNED_KEY_GEN_BATCH_SIZE, kind);
await this.server.registerKeys(keys, kind);
await this.confirmKeys(keys, kind);
})
);
await Promise.all(
[UUIDKind.ACI, UUIDKind.PNI].map(async kind => {
const keys = await this.generateKeys(
SIGNED_KEY_GEN_BATCH_SIZE,
kind
);
await this.server.registerKeys(keys, kind);
await this.confirmKeys(keys, kind);
})
);
} finally {
this.server.finishRegistration(registrationBaton);
}
await this.registrationDone();
});
}
@ -276,23 +284,30 @@ export default class AccountManager extends EventTarget {
);
}
await this.createAccount({
number: provisionMessage.number,
verificationCode: provisionMessage.provisioningCode,
identityKeyPair: provisionMessage.identityKeyPair,
profileKey: provisionMessage.profileKey,
deviceName,
userAgent: provisionMessage.userAgent,
readReceipts: provisionMessage.readReceipts,
});
await clearSessionsAndPreKeys();
// TODO: DESKTOP-2794
const keys = await this.generateKeys(
SIGNED_KEY_GEN_BATCH_SIZE,
UUIDKind.ACI
);
await this.server.registerKeys(keys, UUIDKind.ACI);
await this.confirmKeys(keys, UUIDKind.ACI);
const registrationBaton = this.server.startRegistration();
try {
await this.createAccount({
number: provisionMessage.number,
verificationCode: provisionMessage.provisioningCode,
identityKeyPair: provisionMessage.identityKeyPair,
profileKey: provisionMessage.profileKey,
deviceName,
userAgent: provisionMessage.userAgent,
readReceipts: provisionMessage.readReceipts,
});
await clearSessionsAndPreKeys();
// TODO: DESKTOP-2794
const keys = await this.generateKeys(
SIGNED_KEY_GEN_BATCH_SIZE,
UUIDKind.ACI
);
await this.server.registerKeys(keys, UUIDKind.ACI);
await this.confirmKeys(keys, UUIDKind.ACI);
} finally {
this.server.finishRegistration(registrationBaton);
}
await this.registrationDone();
});
}

View File

@ -28,6 +28,8 @@ import type { Readable } from 'stream';
import { assert, strictAssert } from '../util/assert';
import { isRecord } from '../util/isRecord';
import * as durations from '../util/durations';
import type { ExplodePromiseResultType } from '../util/explodePromise';
import { explodePromise } from '../util/explodePromise';
import { getUserAgent } from '../util/getUserAgent';
import { getStreamWithTimeout } from '../util/getStreamWithTimeout';
import { formatAcceptLanguageHeader } from '../util/userLanguages';
@ -633,6 +635,7 @@ type AjaxOptionsType = {
urlParameters?: string;
username?: string;
validateResponse?: any;
isRegistration?: true;
} & (
| {
unauthenticated?: false;
@ -766,6 +769,8 @@ export type GetUuidsForE164sV2OptionsType = Readonly<{
}>;
export type WebAPIType = {
startRegistration(): unknown;
finishRegistration(baton: unknown): void;
confirmCode: (
number: string,
code: string,
@ -1067,6 +1072,8 @@ export function initialize({
const PARSE_GROUP_LOG_RANGE_HEADER =
/$versions (\d{1,10})-(\d{1,10})\/(d{1,10})/;
let activeRegistration: ExplodePromiseResultType<void> | undefined;
const socketManager = new SocketManager({
url,
certificateAuthority,
@ -1117,6 +1124,7 @@ export function initialize({
confirmCode,
createGroup,
deleteUsername,
finishRegistration,
fetchLinkPreviewImage,
fetchLinkPreviewMetadata,
getAttachment,
@ -1165,6 +1173,7 @@ export function initialize({
sendMessagesUnauth,
sendWithSenderKey,
setSignedPreKey,
startRegistration,
updateDeviceName,
uploadAvatar,
uploadGroupAvatar,
@ -1186,6 +1195,18 @@ export function initialize({
): Promise<unknown>;
async function _ajax(param: AjaxOptionsType): Promise<unknown> {
if (
!param.unauthenticated &&
activeRegistration &&
!param.isRegistration
) {
log.info('WebAPI: request blocked by active registration');
const start = Date.now();
await activeRegistration.promise;
const duration = Date.now() - start;
log.info(`WebAPI: request unblocked after ${duration}ms`);
}
if (!param.urlParameters) {
param.urlParameters = '';
}
@ -1635,6 +1656,31 @@ export function initialize({
}
}
function startRegistration() {
strictAssert(
activeRegistration === undefined,
'Registration already in progress'
);
activeRegistration = explodePromise<void>();
log.info('WebAPI: starting registration');
return activeRegistration;
}
function finishRegistration(registration: unknown) {
strictAssert(activeRegistration !== undefined, 'No active registration');
strictAssert(
activeRegistration === registration,
'Invalid registration baton'
);
log.info('WebAPI: finishing registration');
const current = activeRegistration;
activeRegistration = undefined;
current.resolve();
}
async function confirmCode(
number: string,
code: string,
@ -1677,6 +1723,7 @@ export function initialize({
password = newPassword;
const response = (await _ajax({
isRegistration: true,
call,
httpType: 'PUT',
responseType: 'json',
@ -1749,6 +1796,7 @@ export function initialize({
};
await _ajax({
isRegistration: true,
call: 'keys',
urlParameters: `?${uuidKindToQuery(uuidKind)}`,
httpType: 'PUT',

View File

@ -1,11 +1,13 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export function explodePromise<T>(): {
export type ExplodePromiseResultType<T> = Readonly<{
promise: Promise<T>;
resolve: (value: T) => void;
reject: (error: Error) => void;
} {
}>;
export function explodePromise<T>(): ExplodePromiseResultType<T> {
let resolve: (value: T) => void;
let reject: (error: Error) => void;