From 7c01b9e6589d6d5d5bcf92bef74feb46c54123af Mon Sep 17 00:00:00 2001 From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Date: Thu, 1 Oct 2020 16:13:47 +0200 Subject: [PATCH] Refactor open window (#473) * add mnemonic redirect like json * undefined not allowed because request is a defined object * Apply suggestions from code review Co-authored-by: Jaco Greeff * remove redirect Co-authored-by: Jaco Greeff --- .../src/background/handlers/Extension.ts | 24 ++++++++++--------- .../extension-base/src/background/types.ts | 7 ++++-- packages/extension-base/src/defaults.ts | 2 ++ packages/extension-ui/src/hooks/useIsPopup.ts | 10 ++++++++ packages/extension-ui/src/messaging.ts | 10 +++----- .../extension-ui/src/partials/MenuAdd.tsx | 10 ++++---- .../src/partials/MenuSettings.tsx | 10 ++++++-- 7 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 packages/extension-ui/src/hooks/useIsPopup.ts diff --git a/packages/extension-base/src/background/handlers/Extension.ts b/packages/extension-base/src/background/handlers/Extension.ts index 91e1677c..4672c84a 100644 --- a/packages/extension-base/src/background/handlers/Extension.ts +++ b/packages/extension-base/src/background/handlers/Extension.ts @@ -3,9 +3,9 @@ import { MetadataDef } from '@polkadot/extension-inject/types'; import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; -import { AccountJson, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestDeriveCreate, ResponseDeriveValidate, RequestMetadataApprove, RequestMetadataReject, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestSeedCreate, RequestTypes, ResponseAccountExport, RequestAccountForget, ResponseSeedCreate, RequestSeedValidate, RequestDeriveValidate, ResponseSeedValidate, ResponseType, SigningRequest, RequestJsonRestore, ResponseJsonRestore, RequestAccountChangePassword } from '../types'; +import { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeApprove, RequestAuthorizeReject, RequestDeriveCreate, ResponseDeriveValidate, RequestMetadataApprove, RequestMetadataReject, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestSeedCreate, RequestTypes, ResponseAccountExport, RequestAccountForget, ResponseSeedCreate, RequestSeedValidate, RequestDeriveValidate, ResponseSeedValidate, ResponseType, SigningRequest, RequestJsonRestore, ResponseJsonRestore, RequestAccountChangePassword } from '../types'; -import { PASSWORD_EXPIRY_MS } from '@polkadot/extension-base/defaults'; +import { ALLOWED_PATH, PASSWORD_EXPIRY_MS } from '@polkadot/extension-base/defaults'; import chrome from '@polkadot/extension-inject/chrome'; import keyring from '@polkadot/ui-keyring'; import accountsObservable from '@polkadot/ui-keyring/observable/accounts'; @@ -377,12 +377,17 @@ export default class Extension { return true; } - private windowOpen (path = '/'): boolean { - console.error('open', `${chrome.extension.getURL('index.html')}#${path}`); + private windowOpen (path: AllowedPath): boolean { + const url = `${chrome.extension.getURL('index.html')}#${path}`; - chrome.tabs.create({ - url: `${chrome.extension.getURL('index.html')}#${path}` - }); + if (!ALLOWED_PATH.includes(path)) { + console.error('Not allowed to open the url:', url); + + return false; + } + + console.log('open', url); + chrome.tabs.create({ url }); return true; } @@ -520,10 +525,7 @@ export default class Extension { return this.signingSubscribe(id, port); case 'pri(window.open)': - return this.windowOpen(); - - case 'pri(window.open.json)': - return this.windowOpen('/account/restore-json'); + return this.windowOpen(request as AllowedPath); default: throw new Error(`Unable to handle message of type ${type}`); diff --git a/packages/extension-base/src/background/types.ts b/packages/extension-base/src/background/types.ts index 4ece7d7d..250b6c7e 100644 --- a/packages/extension-base/src/background/types.ts +++ b/packages/extension-base/src/background/types.ts @@ -10,6 +10,8 @@ import { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@polkadot/keyri import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; import { TypeRegistry } from '@polkadot/types'; +import { ALLOWED_PATH } from '../defaults'; + type KeysWithDefinedValues = { [K in keyof T]: T[K] extends undefined ? never : K }[keyof T]; @@ -97,8 +99,7 @@ export interface RequestSignatures { 'pri(signing.cancel)': [RequestSigningCancel, boolean]; 'pri(signing.isLocked)': [RequestSigningIsLocked, boolean]; 'pri(signing.requests)': [RequestSigningSubscribe, boolean, SigningRequest[]]; - 'pri(window.open)': [null, boolean]; - 'pri(window.open.json)': [null, boolean]; + 'pri(window.open)': [AllowedPath, boolean]; // public/external requests, i.e. from a page 'pub(accounts.list)': [RequestAccountList, InjectedAccount[]]; 'pub(accounts.subscribe)': [RequestAccountSubscribe, boolean, InjectedAccount[]]; @@ -348,3 +349,5 @@ export interface RequestJsonRestore { export interface ResponseJsonRestore { error: string | null; } + +export type AllowedPath = typeof ALLOWED_PATH[number]; diff --git a/packages/extension-base/src/defaults.ts b/packages/extension-base/src/defaults.ts index dc05de6c..50202798 100644 --- a/packages/extension-base/src/defaults.ts +++ b/packages/extension-base/src/defaults.ts @@ -1,12 +1,14 @@ // Copyright 2019-2020 @polkadot/extension-base authors & contributors // SPDX-License-Identifier: Apache-2.0 +const ALLOWED_PATH = ['/', '/account/restore-json'] as const; const PORT_CONTENT = 'content'; const PORT_EXTENSION = 'extension'; const PASSWORD_EXPIRY_MIN = 15; const PASSWORD_EXPIRY_MS = PASSWORD_EXPIRY_MIN * 60 * 1000; export { + ALLOWED_PATH, PASSWORD_EXPIRY_MIN, PASSWORD_EXPIRY_MS, PORT_CONTENT, diff --git a/packages/extension-ui/src/hooks/useIsPopup.ts b/packages/extension-ui/src/hooks/useIsPopup.ts new file mode 100644 index 00000000..fd974802 --- /dev/null +++ b/packages/extension-ui/src/hooks/useIsPopup.ts @@ -0,0 +1,10 @@ +// Copyright 2019-2020 @polkadot/extension-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { useMemo } from 'react'; + +export default function useIsPopup (): boolean { + return useMemo(() => { + return window.innerWidth <= 560; + }, []); +} diff --git a/packages/extension-ui/src/messaging.ts b/packages/extension-ui/src/messaging.ts index b2052467..e4e00b3f 100644 --- a/packages/extension-ui/src/messaging.ts +++ b/packages/extension-ui/src/messaging.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Message } from '@polkadot/extension-base/types'; -import { AccountJson, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions, ResponseDeriveValidate, ResponseJsonRestore } from '@polkadot/extension-base/background/types'; +import { AccountJson, AllowedPath, AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MetadataRequest, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions, ResponseDeriveValidate, ResponseJsonRestore } from '@polkadot/extension-base/background/types'; import { Chain } from '@polkadot/extension-chains/types'; import { MetadataDef } from '@polkadot/extension-inject/types'; import { KeypairType } from '@polkadot/util-crypto/types'; @@ -186,10 +186,6 @@ export async function validateSeed (suri: string, type?: KeypairType): Promise<{ return sendMessage('pri(seed.validate)', { suri, type }); } -export async function windowOpen (): Promise { - return sendMessage('pri(window.open)', null); -} - export async function validateDerivationPath (parentAddress: string, suri: string, parentPassword: string): Promise { return sendMessage('pri(derivation.validate)', { parentAddress, parentPassword, suri }); } @@ -198,8 +194,8 @@ export async function deriveAccount (parentAddress: string, suri: string, parent return sendMessage('pri(derivation.create)', { genesisHash, name, parentAddress, parentPassword, password, suri }); } -export async function jsonRestoreWindowOpen (): Promise { - return sendMessage('pri(window.open.json)', null); +export async function windowOpen (path: AllowedPath): Promise { + return sendMessage('pri(window.open)', path); } export async function jsonVerifyFile (json: KeyringPair$Json): Promise { diff --git a/packages/extension-ui/src/partials/MenuAdd.tsx b/packages/extension-ui/src/partials/MenuAdd.tsx index 84a9943e..6c35f986 100644 --- a/packages/extension-ui/src/partials/MenuAdd.tsx +++ b/packages/extension-ui/src/partials/MenuAdd.tsx @@ -11,23 +11,25 @@ import plusIcon from '../assets/plus.svg'; import qrIcon from '../assets/qr.svg'; import seedIcon from '../assets/secret.svg'; import { AccountContext, Link, MediaContext, Menu, MenuDivider, MenuItem, Svg } from '../components'; +import useIsPopup from '../hooks/useIsPopup'; import useTranslation from '../hooks/useTranslation'; -import { jsonRestoreWindowOpen } from '../messaging'; +import { windowOpen } from '../messaging'; interface Props extends ThemeProps { className?: string; reference: React.MutableRefObject; } -const isPopup = window.innerWidth <= 480; +const jsonPath = '/account/restore-json'; function MenuAdd ({ className, reference }: Props): React.ReactElement { const { t } = useTranslation(); const { master } = useContext(AccountContext); const mediaAllowed = useContext(MediaContext); + const isPopup = useIsPopup(); const _openJson = useCallback((): void => { - jsonRestoreWindowOpen().catch(console.error); + windowOpen(jsonPath).catch(console.error); }, []); return ( @@ -55,7 +57,7 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement { {t('Restore account from backup JSON file')} diff --git a/packages/extension-ui/src/partials/MenuSettings.tsx b/packages/extension-ui/src/partials/MenuSettings.tsx index d41f3642..7092e5d0 100644 --- a/packages/extension-ui/src/partials/MenuSettings.tsx +++ b/packages/extension-ui/src/partials/MenuSettings.tsx @@ -9,6 +9,7 @@ import settings from '@polkadot/ui-settings'; import FullScreenIcon from '../assets/fullscreen.svg'; import { ActionText, Checkbox, Dropdown, Menu, MenuDivider, MenuItem, Svg, Switch, ThemeSwitchContext, themes } from '../components'; +import useIsPopup from '../hooks/useIsPopup'; import useTranslation from '../hooks/useTranslation'; import { windowOpen } from '../messaging'; import getLanguageOptions from '../util/getLanguageOptions'; @@ -23,7 +24,6 @@ interface Props extends ThemeProps { reference: React.MutableRefObject; } -const isPopup = window.innerWidth <= 560; const prefixOptions = settings.availablePrefixes .filter(({ value }) => value !== -1) .map(({ text, value }): Option => ({ text, value: `${value}` })); @@ -34,6 +34,7 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement(ThemeContext); const setTheme = useContext(ThemeSwitchContext); + const isPopup = useIsPopup(); const languageOptions = useMemo(() => getLanguageOptions(), []); useEffect(() => { @@ -53,6 +54,11 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement windowOpen('/'), + [] + ); + const _onChangeLang = useCallback( (value: string): void => { settings.set({ i18nLang: value }); @@ -121,7 +127,7 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement('Open extension in new window')} />