From 8c33d89057a03a5cd4802ab13d4615a2bfabcfd1 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 4 Mar 2021 11:06:44 +1100 Subject: [PATCH] make AccountManager functions only rather than full static class --- ts/components/session/ActionsPanel.tsx | 4 +- .../session/LeftPaneSettingSection.tsx | 4 +- .../session/SessionIDResetDialog.tsx | 4 +- .../session/registration/RegistrationTabs.tsx | 22 +- ts/util/accountManager.ts | 347 +++++++++--------- ts/util/index.ts | 1 - 6 files changed, 182 insertions(+), 200 deletions(-) diff --git a/ts/components/session/ActionsPanel.tsx b/ts/components/session/ActionsPanel.tsx index e583bb8da..1223b7116 100644 --- a/ts/components/session/ActionsPanel.tsx +++ b/ts/components/session/ActionsPanel.tsx @@ -22,7 +22,7 @@ import { } from '../../data/data'; import { OnionPaths } from '../../session/onions'; import { getMessageQueue } from '../../session/sending'; -import { AccountManager } from '../../util'; +import { clearSessionsAndPreKeys } from '../../util/accountManager'; // tslint:disable-next-line: no-import-side-effect no-submodule-imports export enum SectionType { @@ -90,7 +90,7 @@ class ActionsPanelPrivate extends React.Component { void this.showResetSessionIDDialogIfNeeded(); // remove existing prekeys, sign prekeys and sessions - void AccountManager.clearSessionsAndPreKeys(); + void clearSessionsAndPreKeys(); // Do this only if we created a new Session ID, or if we already received the initial configuration message diff --git a/ts/components/session/LeftPaneSettingSection.tsx b/ts/components/session/LeftPaneSettingSection.tsx index 241e41921..fdb781ba1 100644 --- a/ts/components/session/LeftPaneSettingSection.tsx +++ b/ts/components/session/LeftPaneSettingSection.tsx @@ -14,7 +14,7 @@ import { SessionSearchInput } from './SessionSearchInput'; import { SessionSettingCategory } from './settings/SessionSettings'; import { DefaultTheme } from 'styled-components'; import { LeftPaneSectionHeader } from './LeftPaneSectionHeader'; -import { AccountManager } from '../../util'; +import { deleteAccount } from '../../util/accountManager'; interface Props { settingsCategory: SessionSettingCategory; @@ -180,7 +180,7 @@ export class LeftPaneSettingSection extends React.Component { title, message, messageSub, - resolve: AccountManager.deleteAccount, + resolve: deleteAccount, okTheme: 'danger', }); } diff --git a/ts/components/session/SessionIDResetDialog.tsx b/ts/components/session/SessionIDResetDialog.tsx index fe2c658db..4763f86a7 100644 --- a/ts/components/session/SessionIDResetDialog.tsx +++ b/ts/components/session/SessionIDResetDialog.tsx @@ -4,7 +4,7 @@ import { SessionModal } from './SessionModal'; import { SessionButton, SessionButtonColor } from './SessionButton'; import { DefaultTheme, withTheme } from 'styled-components'; import { SessionIcon, SessionIconSize, SessionIconType } from './icon'; -import { AccountManager } from '../../util'; +import { deleteAccount } from '../../util/accountManager'; type Props = { onClose: any; @@ -41,7 +41,7 @@ const SessionIDResetDialogInner = (props: Props) => { { - void AccountManager.deleteAccount('Session ID Upgrade'); + void deleteAccount('Session ID Upgrade'); props.onClose(); }} buttonColor={SessionButtonColor.Danger} diff --git a/ts/components/session/registration/RegistrationTabs.tsx b/ts/components/session/registration/RegistrationTabs.tsx index 7056f5583..f59a6d710 100644 --- a/ts/components/session/registration/RegistrationTabs.tsx +++ b/ts/components/session/registration/RegistrationTabs.tsx @@ -14,10 +14,12 @@ import { TabLabel, TabType } from './TabLabel'; import { PasswordUtil } from '../../../util'; import { trigger } from '../../../shims/events'; import { - AccountManager, + generateMnemonic, + registerSingleDevice, sessionGenerateKeyPair, + signInByLinkingDevice, } from '../../../util/accountManager'; -import { fromHex, fromHexToArray } from '../../../session/utils/String'; +import { fromHex } from '../../../session/utils/String'; import { TaskTimedOutError } from '../../../session/utils/Promise'; export const MAX_USERNAME_LENGTH = 20; @@ -138,11 +140,7 @@ export async function signUp(signUpDetails: { try { await resetRegistration(); await window.setPassword(password); - await AccountManager.registerSingleDevice( - generatedRecoveryPhrase, - 'english', - trimName - ); + await registerSingleDevice(generatedRecoveryPhrase, 'english', trimName); await createOrUpdateItem({ id: 'hasSyncedInitialConfigurationItem', value: true, @@ -191,11 +189,7 @@ export async function signInWithRecovery(signInDetails: { await resetRegistration(); await window.setPassword(password); - await AccountManager.registerSingleDevice( - userRecoveryPhrase, - 'english', - trimName - ); + await registerSingleDevice(userRecoveryPhrase, 'english', trimName); trigger('openInbox'); } catch (e) { await resetRegistration(); @@ -225,7 +219,7 @@ export async function signInWithLinking(signInDetails: { try { await resetRegistration(); await window.setPassword(password); - await AccountManager.signInByLinkingDevice(userRecoveryPhrase, 'english'); + await signInByLinkingDevice(userRecoveryPhrase, 'english'); let displayNameFromNetwork = ''; @@ -311,7 +305,7 @@ export class RegistrationTabs extends React.Component { private async generateMnemonicAndKeyPair() { if (this.state.generatedRecoveryPhrase === '') { const language = 'english'; - const mnemonic = await AccountManager.generateMnemonic(language); + const mnemonic = await generateMnemonic(language); let seedHex = window.mnemonic.mn_decode(mnemonic, language); // handle shorter than 32 bytes seeds diff --git a/ts/util/accountManager.ts b/ts/util/accountManager.ts index 93146342c..ec1e2a95a 100644 --- a/ts/util/accountManager.ts +++ b/ts/util/accountManager.ts @@ -59,203 +59,192 @@ const generateKeypair = async (mnemonic: string, mnemonicLanguage: string) => { return sessionGenerateKeyPair(seed); }; -// TODO not sure why AccountManager was a singleton before. Can we get rid of it as a singleton? -// tslint:disable-next-line: no-unnecessary-class -export class AccountManager { - /** - * Sign in with a recovery phrase. We won't try to recover an existing profile name - * @param mnemonic the mnemonic the user duly saved in a safe place. We will restore his sessionID based on this. - * @param mnemonicLanguage 'english' only is supported - * @param profileName the displayName to use for this user - */ - public static async signInWithRecovery( - mnemonic: string, - mnemonicLanguage: string, - profileName: string - ) { - return AccountManager.registerSingleDevice( - mnemonic, - mnemonicLanguage, - profileName +/** + * Sign in with a recovery phrase. We won't try to recover an existing profile name + * @param mnemonic the mnemonic the user duly saved in a safe place. We will restore his sessionID based on this. + * @param mnemonicLanguage 'english' only is supported + * @param profileName the displayName to use for this user + */ +export async function signInWithRecovery( + mnemonic: string, + mnemonicLanguage: string, + profileName: string +) { + return registerSingleDevice(mnemonic, mnemonicLanguage, profileName); +} + +/** + * Sign in with a recovery phrase but trying to recover display name and avatar from the first encountered configuration message. + * @param mnemonic the mnemonic the user duly saved in a safe place. We will restore his sessionID based on this. + * @param mnemonicLanguage 'english' only is supported + */ +export async function signInByLinkingDevice( + mnemonic: string, + mnemonicLanguage: string +) { + if (!mnemonic) { + throw new Error( + 'Session always needs a mnemonic. Either generated or given by the user' ); } + if (!mnemonicLanguage) { + throw new Error('We always needs a mnemonicLanguage'); + } - /** - * Sign in with a recovery phrase but trying to recover display name and avatar from the first encountered configuration message. - * @param mnemonic the mnemonic the user duly saved in a safe place. We will restore his sessionID based on this. - * @param mnemonicLanguage 'english' only is supported - */ - public static async signInByLinkingDevice( - mnemonic: string, - mnemonicLanguage: string - ) { - if (!mnemonic) { - throw new Error( - 'Session always needs a mnemonic. Either generated or given by the user' - ); - } - if (!mnemonicLanguage) { - throw new Error('We always needs a mnemonicLanguage'); - } - - const identityKeyPair = await generateKeypair(mnemonic, mnemonicLanguage); - UserUtils.setSignInByLinking(true); - await AccountManager.createAccount(identityKeyPair); - UserUtils.saveRecoveryPhrase(mnemonic); - await AccountManager.clearSessionsAndPreKeys(); - const pubKeyString = toHex(identityKeyPair.pubKey); + const identityKeyPair = await generateKeypair(mnemonic, mnemonicLanguage); + UserUtils.setSignInByLinking(true); + await createAccount(identityKeyPair); + UserUtils.saveRecoveryPhrase(mnemonic); + await clearSessionsAndPreKeys(); + const pubKeyString = toHex(identityKeyPair.pubKey); - // await for the first configuration message to come in. - await AccountManager.registrationDone(pubKeyString, ''); + // await for the first configuration message to come in. + await registrationDone(pubKeyString, ''); +} +/** + * This is a signup. User has no recovery and does not try to link a device + * @param mnemonic The mnemonic generated on first app loading and to use for this brand new user + * @param mnemonicLanguage only 'english' is supported + * @param profileName the display name to register toi + */ +export async function registerSingleDevice( + generatedMnemonic: string, + mnemonicLanguage: string, + profileName: string +) { + if (!generatedMnemonic) { + throw new Error( + 'Session always needs a mnemonic. Either generated or given by the user' + ); + } + if (!profileName) { + throw new Error('We always needs a profileName'); + } + if (!mnemonicLanguage) { + throw new Error('We always needs a mnemonicLanguage'); } - /** - * This is a signup. User has no recovery and does not try to link a device - * @param mnemonic The mnemonic generated on first app loading and to use for this brand new user - * @param mnemonicLanguage only 'english' is supported - * @param profileName the display name to register toi - */ - public static async registerSingleDevice( - generatedMnemonic: string, - mnemonicLanguage: string, - profileName: string - ) { - if (!generatedMnemonic) { - throw new Error( - 'Session always needs a mnemonic. Either generated or given by the user' - ); - } - if (!profileName) { - throw new Error('We always needs a profileName'); - } - if (!mnemonicLanguage) { - throw new Error('We always needs a mnemonicLanguage'); - } - const identityKeyPair = await generateKeypair( - generatedMnemonic, - mnemonicLanguage - ); + const identityKeyPair = await generateKeypair( + generatedMnemonic, + mnemonicLanguage + ); - await AccountManager.createAccount(identityKeyPair); - UserUtils.saveRecoveryPhrase(generatedMnemonic); - await AccountManager.clearSessionsAndPreKeys(); - await UserUtils.setLastProfileUpdateTimestamp(Date.now()); + await createAccount(identityKeyPair); + UserUtils.saveRecoveryPhrase(generatedMnemonic); + await clearSessionsAndPreKeys(); + await UserUtils.setLastProfileUpdateTimestamp(Date.now()); - const pubKeyString = toHex(identityKeyPair.pubKey); - await AccountManager.registrationDone(pubKeyString, profileName); - } + const pubKeyString = toHex(identityKeyPair.pubKey); + await registrationDone(pubKeyString, profileName); +} - public static async generateMnemonic(language = 'english') { - // Note: 4 bytes are converted into 3 seed words, so length 12 seed words - // (13 - 1 checksum) are generated using 12 * 4 / 3 = 16 bytes. - const seedSize = 16; - const seed = (await getSodium()).randombytes_buf(seedSize); - const hex = toHex(seed); - return window.mnemonic.mn_encode(hex, language); - } +export async function generateMnemonic(language = 'english') { + // Note: 4 bytes are converted into 3 seed words, so length 12 seed words + // (13 - 1 checksum) are generated using 12 * 4 / 3 = 16 bytes. + const seedSize = 16; + const seed = (await getSodium()).randombytes_buf(seedSize); + const hex = toHex(seed); + return window.mnemonic.mn_encode(hex, language); +} - public static async clearSessionsAndPreKeys() { - window.log.info('clearing all sessions'); - // During secondary device registration we need to keep our prekeys sent - // to other pubkeys - await Promise.all([ - removeAllPreKeys(), - removeAllSignedPreKeys(), - removeAllContactPreKeys(), - removeAllContactSignedPreKeys(), - removeAllSessions(), - ]); - } +export async function clearSessionsAndPreKeys() { + window.log.info('clearing all sessions'); + // During secondary device registration we need to keep our prekeys sent + // to other pubkeys + await Promise.all([ + removeAllPreKeys(), + removeAllSignedPreKeys(), + removeAllContactPreKeys(), + removeAllContactSignedPreKeys(), + removeAllSessions(), + ]); +} - public static async deleteAccount(reason?: string) { - const deleteEverything = async () => { - window.log.info( - 'configuration message sent successfully. Deleting everything' - ); - await window.Signal.Logs.deleteAll(); - await window.Signal.Data.removeAll(); - await window.Signal.Data.close(); - await window.Signal.Data.removeDB(); - await window.Signal.Data.removeOtherData(); - // 'unlink' => toast will be shown on app restart - window.localStorage.setItem('restart-reason', reason || ''); - }; +export async function deleteAccount(reason?: string) { + const deleteEverything = async () => { + window.log.info( + 'configuration message sent successfully. Deleting everything' + ); + await window.Signal.Logs.deleteAll(); + await window.Signal.Data.removeAll(); + await window.Signal.Data.close(); + await window.Signal.Data.removeDB(); + await window.Signal.Data.removeOtherData(); + // 'unlink' => toast will be shown on app restart + window.localStorage.setItem('restart-reason', reason || ''); + }; + try { + window.log.info('DeleteAccount => Sending a last SyncConfiguration'); + // be sure to wait for the message being effectively sent. Otherwise we won't be able to encrypt it for our devices ! + await forceSyncConfigurationNowIfNeeded(true); + window.log.info('Last configuration message sent!'); + await deleteEverything(); + } catch (error) { + window.log.error( + 'Something went wrong deleting all data:', + error && error.stack ? error.stack : error + ); try { - window.log.info('DeleteAccount => Sending a last SyncConfiguration'); - // be sure to wait for the message being effectively sent. Otherwise we won't be able to encrypt it for our devices ! - await forceSyncConfigurationNowIfNeeded(true); - window.log.info('Last configuration message sent!'); await deleteEverything(); - } catch (error) { - window.log.error( - 'Something went wrong deleting all data:', - error && error.stack ? error.stack : error - ); - try { - await deleteEverything(); - } catch (e) { - window.log.error(e); - } + } catch (e) { + window.log.error(e); } - window.restart(); } + window.restart(); +} - private static async createAccount(identityKeyPair: any) { - const sodium = await getSodium(); - let password = fromArrayBufferToBase64(sodium.randombytes_buf(16)); - password = password.substring(0, password.length - 2); - - await Promise.all([ - window.textsecure.storage.remove('identityKey'), - window.textsecure.storage.remove('signaling_key'), - window.textsecure.storage.remove('password'), - window.textsecure.storage.remove('registrationId'), - window.textsecure.storage.remove('number_id'), - window.textsecure.storage.remove('device_name'), - window.textsecure.storage.remove('userAgent'), - window.textsecure.storage.remove('read-receipt-setting'), - window.textsecure.storage.remove('typing-indicators-setting'), - window.textsecure.storage.remove('regionCode'), - ]); - - // update our own identity key, which may have changed - // if we're relinking after a reinstall on the master device - const pubKeyString = toHex(identityKeyPair.pubKey); - - await window.textsecure.storage.put('identityKey', identityKeyPair); - await window.textsecure.storage.put('password', password); - await window.textsecure.storage.put('read-receipt-setting', false); - - // Enable typing indicators by default - await window.textsecure.storage.put( - 'typing-indicators-setting', - Boolean(true) - ); +async function createAccount(identityKeyPair: any) { + const sodium = await getSodium(); + let password = fromArrayBufferToBase64(sodium.randombytes_buf(16)); + password = password.substring(0, password.length - 2); + + await Promise.all([ + window.textsecure.storage.remove('identityKey'), + window.textsecure.storage.remove('signaling_key'), + window.textsecure.storage.remove('password'), + window.textsecure.storage.remove('registrationId'), + window.textsecure.storage.remove('number_id'), + window.textsecure.storage.remove('device_name'), + window.textsecure.storage.remove('userAgent'), + window.textsecure.storage.remove('read-receipt-setting'), + window.textsecure.storage.remove('typing-indicators-setting'), + window.textsecure.storage.remove('regionCode'), + ]); + + // update our own identity key, which may have changed + // if we're relinking after a reinstall on the master device + const pubKeyString = toHex(identityKeyPair.pubKey); + + await window.textsecure.storage.put('identityKey', identityKeyPair); + await window.textsecure.storage.put('password', password); + await window.textsecure.storage.put('read-receipt-setting', false); + + // Enable typing indicators by default + await window.textsecure.storage.put( + 'typing-indicators-setting', + Boolean(true) + ); - await window.textsecure.storage.user.setNumberAndDeviceId(pubKeyString, 1); - } + await window.textsecure.storage.user.setNumberAndDeviceId(pubKeyString, 1); +} - private static async registrationDone( - ourPubkey: string, - displayName: string - ) { - window.log.info('registration done'); +async function registrationDone(ourPubkey: string, displayName: string) { + window.log.info('registration done'); - window.textsecure.storage.put('primaryDevicePubKey', ourPubkey); + window.textsecure.storage.put('primaryDevicePubKey', ourPubkey); - // Ensure that we always have a conversation for ourself - const conversation = await ConversationController.getInstance().getOrCreateAndWait( - ourPubkey, - 'private' - ); - await conversation.setLokiProfile({ displayName }); - const user = { - ourNumber: getOurPubKeyStrFromCache(), - ourPrimary: window.textsecure.storage.get('primaryDevicePubKey'), - }; - trigger('userChanged', user); - window.Whisper.Registration.markDone(); - window.log.info('dispatching registration event'); - trigger('registration_done'); - } + // Ensure that we always have a conversation for ourself + const conversation = await ConversationController.getInstance().getOrCreateAndWait( + ourPubkey, + 'private' + ); + await conversation.setLokiProfile({ displayName }); + const user = { + ourNumber: getOurPubKeyStrFromCache(), + ourPrimary: window.textsecure.storage.get('primaryDevicePubKey'), + }; + trigger('userChanged', user); + window.Whisper.Registration.markDone(); + window.log.info('dispatching registration event'); + trigger('registration_done'); } diff --git a/ts/util/index.ts b/ts/util/index.ts index c8ce097ea..214a13908 100644 --- a/ts/util/index.ts +++ b/ts/util/index.ts @@ -10,7 +10,6 @@ import * as AttachmentUtil from './attachmentsUtil'; import * as LinkPreviewUtil from './linkPreviewFetch'; export * from './blockedNumberController'; -export * from './accountManager'; export { arrayBufferToObjectURL,