Block WebAPI during active registration
This commit is contained in:
parent
9e9e5274cf
commit
8070b8b14f
3 changed files with 100 additions and 35 deletions
|
@ -172,24 +172,32 @@ export default class AccountManager extends EventTarget {
|
||||||
const profileKey = getRandomBytes(PROFILE_KEY_LENGTH);
|
const profileKey = getRandomBytes(PROFILE_KEY_LENGTH);
|
||||||
const accessKey = deriveAccessKey(profileKey);
|
const accessKey = deriveAccessKey(profileKey);
|
||||||
|
|
||||||
await this.createAccount({
|
const registrationBaton = this.server.startRegistration();
|
||||||
number,
|
try {
|
||||||
verificationCode,
|
await this.createAccount({
|
||||||
identityKeyPair,
|
number,
|
||||||
pniKeyPair,
|
verificationCode,
|
||||||
profileKey,
|
identityKeyPair,
|
||||||
accessKey,
|
pniKeyPair,
|
||||||
});
|
profileKey,
|
||||||
|
accessKey,
|
||||||
|
});
|
||||||
|
|
||||||
await this.clearSessionsAndPreKeys();
|
await this.clearSessionsAndPreKeys();
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[UUIDKind.ACI, UUIDKind.PNI].map(async kind => {
|
[UUIDKind.ACI, UUIDKind.PNI].map(async kind => {
|
||||||
const keys = await this.generateKeys(SIGNED_KEY_GEN_BATCH_SIZE, kind);
|
const keys = await this.generateKeys(
|
||||||
await this.server.registerKeys(keys, kind);
|
SIGNED_KEY_GEN_BATCH_SIZE,
|
||||||
await this.confirmKeys(keys, kind);
|
kind
|
||||||
})
|
);
|
||||||
);
|
await this.server.registerKeys(keys, kind);
|
||||||
|
await this.confirmKeys(keys, kind);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.server.finishRegistration(registrationBaton);
|
||||||
|
}
|
||||||
await this.registrationDone();
|
await this.registrationDone();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -276,23 +284,30 @@ export default class AccountManager extends EventTarget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.createAccount({
|
const registrationBaton = this.server.startRegistration();
|
||||||
number: provisionMessage.number,
|
|
||||||
verificationCode: provisionMessage.provisioningCode,
|
try {
|
||||||
identityKeyPair: provisionMessage.identityKeyPair,
|
await this.createAccount({
|
||||||
profileKey: provisionMessage.profileKey,
|
number: provisionMessage.number,
|
||||||
deviceName,
|
verificationCode: provisionMessage.provisioningCode,
|
||||||
userAgent: provisionMessage.userAgent,
|
identityKeyPair: provisionMessage.identityKeyPair,
|
||||||
readReceipts: provisionMessage.readReceipts,
|
profileKey: provisionMessage.profileKey,
|
||||||
});
|
deviceName,
|
||||||
await clearSessionsAndPreKeys();
|
userAgent: provisionMessage.userAgent,
|
||||||
// TODO: DESKTOP-2794
|
readReceipts: provisionMessage.readReceipts,
|
||||||
const keys = await this.generateKeys(
|
});
|
||||||
SIGNED_KEY_GEN_BATCH_SIZE,
|
await clearSessionsAndPreKeys();
|
||||||
UUIDKind.ACI
|
// TODO: DESKTOP-2794
|
||||||
);
|
const keys = await this.generateKeys(
|
||||||
await this.server.registerKeys(keys, UUIDKind.ACI);
|
SIGNED_KEY_GEN_BATCH_SIZE,
|
||||||
await this.confirmKeys(keys, UUIDKind.ACI);
|
UUIDKind.ACI
|
||||||
|
);
|
||||||
|
await this.server.registerKeys(keys, UUIDKind.ACI);
|
||||||
|
await this.confirmKeys(keys, UUIDKind.ACI);
|
||||||
|
} finally {
|
||||||
|
this.server.finishRegistration(registrationBaton);
|
||||||
|
}
|
||||||
|
|
||||||
await this.registrationDone();
|
await this.registrationDone();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ import type { Readable } from 'stream';
|
||||||
import { assert, strictAssert } from '../util/assert';
|
import { assert, strictAssert } from '../util/assert';
|
||||||
import { isRecord } from '../util/isRecord';
|
import { isRecord } from '../util/isRecord';
|
||||||
import * as durations from '../util/durations';
|
import * as durations from '../util/durations';
|
||||||
|
import type { ExplodePromiseResultType } from '../util/explodePromise';
|
||||||
|
import { explodePromise } from '../util/explodePromise';
|
||||||
import { getUserAgent } from '../util/getUserAgent';
|
import { getUserAgent } from '../util/getUserAgent';
|
||||||
import { getStreamWithTimeout } from '../util/getStreamWithTimeout';
|
import { getStreamWithTimeout } from '../util/getStreamWithTimeout';
|
||||||
import { formatAcceptLanguageHeader } from '../util/userLanguages';
|
import { formatAcceptLanguageHeader } from '../util/userLanguages';
|
||||||
|
@ -633,6 +635,7 @@ type AjaxOptionsType = {
|
||||||
urlParameters?: string;
|
urlParameters?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
validateResponse?: any;
|
validateResponse?: any;
|
||||||
|
isRegistration?: true;
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
unauthenticated?: false;
|
unauthenticated?: false;
|
||||||
|
@ -766,6 +769,8 @@ export type GetUuidsForE164sV2OptionsType = Readonly<{
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type WebAPIType = {
|
export type WebAPIType = {
|
||||||
|
startRegistration(): unknown;
|
||||||
|
finishRegistration(baton: unknown): void;
|
||||||
confirmCode: (
|
confirmCode: (
|
||||||
number: string,
|
number: string,
|
||||||
code: string,
|
code: string,
|
||||||
|
@ -1067,6 +1072,8 @@ export function initialize({
|
||||||
const PARSE_GROUP_LOG_RANGE_HEADER =
|
const PARSE_GROUP_LOG_RANGE_HEADER =
|
||||||
/$versions (\d{1,10})-(\d{1,10})\/(d{1,10})/;
|
/$versions (\d{1,10})-(\d{1,10})\/(d{1,10})/;
|
||||||
|
|
||||||
|
let activeRegistration: ExplodePromiseResultType<void> | undefined;
|
||||||
|
|
||||||
const socketManager = new SocketManager({
|
const socketManager = new SocketManager({
|
||||||
url,
|
url,
|
||||||
certificateAuthority,
|
certificateAuthority,
|
||||||
|
@ -1117,6 +1124,7 @@ export function initialize({
|
||||||
confirmCode,
|
confirmCode,
|
||||||
createGroup,
|
createGroup,
|
||||||
deleteUsername,
|
deleteUsername,
|
||||||
|
finishRegistration,
|
||||||
fetchLinkPreviewImage,
|
fetchLinkPreviewImage,
|
||||||
fetchLinkPreviewMetadata,
|
fetchLinkPreviewMetadata,
|
||||||
getAttachment,
|
getAttachment,
|
||||||
|
@ -1165,6 +1173,7 @@ export function initialize({
|
||||||
sendMessagesUnauth,
|
sendMessagesUnauth,
|
||||||
sendWithSenderKey,
|
sendWithSenderKey,
|
||||||
setSignedPreKey,
|
setSignedPreKey,
|
||||||
|
startRegistration,
|
||||||
updateDeviceName,
|
updateDeviceName,
|
||||||
uploadAvatar,
|
uploadAvatar,
|
||||||
uploadGroupAvatar,
|
uploadGroupAvatar,
|
||||||
|
@ -1186,6 +1195,18 @@ export function initialize({
|
||||||
): Promise<unknown>;
|
): Promise<unknown>;
|
||||||
|
|
||||||
async function _ajax(param: AjaxOptionsType): 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) {
|
if (!param.urlParameters) {
|
||||||
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(
|
async function confirmCode(
|
||||||
number: string,
|
number: string,
|
||||||
code: string,
|
code: string,
|
||||||
|
@ -1677,6 +1723,7 @@ export function initialize({
|
||||||
password = newPassword;
|
password = newPassword;
|
||||||
|
|
||||||
const response = (await _ajax({
|
const response = (await _ajax({
|
||||||
|
isRegistration: true,
|
||||||
call,
|
call,
|
||||||
httpType: 'PUT',
|
httpType: 'PUT',
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
|
@ -1749,6 +1796,7 @@ export function initialize({
|
||||||
};
|
};
|
||||||
|
|
||||||
await _ajax({
|
await _ajax({
|
||||||
|
isRegistration: true,
|
||||||
call: 'keys',
|
call: 'keys',
|
||||||
urlParameters: `?${uuidKindToQuery(uuidKind)}`,
|
urlParameters: `?${uuidKindToQuery(uuidKind)}`,
|
||||||
httpType: 'PUT',
|
httpType: 'PUT',
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
export function explodePromise<T>(): {
|
export type ExplodePromiseResultType<T> = Readonly<{
|
||||||
promise: Promise<T>;
|
promise: Promise<T>;
|
||||||
resolve: (value: T) => void;
|
resolve: (value: T) => void;
|
||||||
reject: (error: Error) => void;
|
reject: (error: Error) => void;
|
||||||
} {
|
}>;
|
||||||
|
|
||||||
|
export function explodePromise<T>(): ExplodePromiseResultType<T> {
|
||||||
let resolve: (value: T) => void;
|
let resolve: (value: T) => void;
|
||||||
let reject: (error: Error) => void;
|
let reject: (error: Error) => void;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue