From 8831d4cbac675582ef6c506b4592376050ee692a Mon Sep 17 00:00:00 2001 From: Keith Date: Sat, 25 May 2019 23:42:58 +0800 Subject: [PATCH] refactor(neuron-wallet): use Validate decorator to verify password in the key module --- packages/neuron-wallet/src/channel/wallet.ts | 3 +- .../neuron-wallet/src/controllers/helpers.ts | 2 +- .../neuron-wallet/src/controllers/index.ts | 6 -- .../neuron-wallet/src/controllers/networks.ts | 2 +- .../src/controllers/transactions.ts | 2 +- .../neuron-wallet/src/controllers/wallets.ts | 11 +-- .../neuron-wallet/src/decorators/errors.ts | 10 +-- .../neuron-wallet/src/decorators/index.ts | 9 ++- .../src/decorators/validators.ts | 75 +++++++++++++++++++ packages/neuron-wallet/src/keys/key.ts | 27 +++---- .../neuron-wallet/src/services/networks.ts | 3 +- .../neuron-wallet/src/startup/initWindow.ts | 4 +- packages/neuron-wallet/src/utils/const.ts | 6 ++ .../neuron-wallet/src/utils/validators.ts | 35 --------- packages/neuron-wallet/tests/keys/key.test.ts | 2 +- .../tests/utils/validators.test.ts | 2 +- 16 files changed, 114 insertions(+), 85 deletions(-) create mode 100644 packages/neuron-wallet/src/decorators/validators.ts delete mode 100644 packages/neuron-wallet/src/utils/validators.ts diff --git a/packages/neuron-wallet/src/channel/wallet.ts b/packages/neuron-wallet/src/channel/wallet.ts index c4f2ce8eb0..a1b2586a22 100644 --- a/packages/neuron-wallet/src/channel/wallet.ts +++ b/packages/neuron-wallet/src/channel/wallet.ts @@ -1,8 +1,7 @@ import { BrowserWindow } from 'electron' import Listeners from './listeners' -import { ResponseCode } from '../controllers' -import { Channel } from '../utils/const' +import { Channel, ResponseCode } from '../utils/const' export default class WalletChannel extends Listeners { public win: BrowserWindow diff --git a/packages/neuron-wallet/src/controllers/helpers.ts b/packages/neuron-wallet/src/controllers/helpers.ts index 2c93882cb0..be1cb2d943 100644 --- a/packages/neuron-wallet/src/controllers/helpers.ts +++ b/packages/neuron-wallet/src/controllers/helpers.ts @@ -1,6 +1,6 @@ import Key from '../keys/key' -import { ResponseCode } from '.' import { CatchControllerError } from '../decorators' +import { ResponseCode } from '../utils/const' import i18n from '../utils/i18n' export enum HelpersMethod { diff --git a/packages/neuron-wallet/src/controllers/index.ts b/packages/neuron-wallet/src/controllers/index.ts index a7cabf8050..965a578e1d 100644 --- a/packages/neuron-wallet/src/controllers/index.ts +++ b/packages/neuron-wallet/src/controllers/index.ts @@ -3,11 +3,6 @@ import WalletsController, { WalletsMethod } from './wallets' import TransactionsController from './transactions' import HelpersController, { HelpersMethod } from './helpers' -export enum ResponseCode { - Fail, - Success, -} - export const methods = { NetworksMethod, WalletsMethod, @@ -19,5 +14,4 @@ export default { WalletsController, TransactionsController, HelpersController, - ResponseCode, } diff --git a/packages/neuron-wallet/src/controllers/networks.ts b/packages/neuron-wallet/src/controllers/networks.ts index 083dadacbe..75462cf8d5 100644 --- a/packages/neuron-wallet/src/controllers/networks.ts +++ b/packages/neuron-wallet/src/controllers/networks.ts @@ -1,6 +1,6 @@ -import { ResponseCode } from '.' import NetworksService, { NetworkType, NetworkID, Network } from '../services/networks' import { CatchControllerError } from '../decorators' +import { ResponseCode } from '../utils/const' import i18n from '../utils/i18n' export enum NetworksMethod { diff --git a/packages/neuron-wallet/src/controllers/transactions.ts b/packages/neuron-wallet/src/controllers/transactions.ts index b2729c5fb6..4785b598bf 100644 --- a/packages/neuron-wallet/src/controllers/transactions.ts +++ b/packages/neuron-wallet/src/controllers/transactions.ts @@ -1,4 +1,3 @@ -import { ResponseCode } from '.' import { Transaction } from '../appTypes/types' import TransactionsService, { TransactionsByAddressesParam, @@ -6,6 +5,7 @@ import TransactionsService, { TransactionsByLockHashesParam, } from '../services/transactions' import { CatchControllerError } from '../decorators' +import { ResponseCode } from '../utils/const' import i18n from '../utils/i18n' /** diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index 35b7751d87..0dc49222bf 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -1,11 +1,9 @@ import WalletsService, { Wallet, WalletProperties } from '../services/wallets' -import { ResponseCode } from './index' -import windowManage from '../utils/windowManage' -import { Channel } from '../utils/const' import Key from '../keys/key' import { CatchControllerError } from '../decorators' +import windowManage from '../utils/windowManage' +import { Channel, ResponseCode } from '../utils/const' import i18n from '../utils/i18n' -import { verifyPasswordComplexity } from '../utils/validators' export enum WalletsMethod { GetAll = 'getAll', @@ -132,7 +130,7 @@ class WalletsController { receivingAddressNumber: number changeAddressNumber: number }): Promise> { - const key = Key.fromKeystore(keystore, password, receivingAddressNumber, changeAddressNumber) + const key = await Key.fromKeystore(keystore, password, receivingAddressNumber, changeAddressNumber) const wallet = WalletsController.service.create({ name, keystore: key.keystore || null, @@ -170,8 +168,7 @@ class WalletsController { if (newPassword) { if (WalletsController.service.validate({ id, password })) { - verifyPasswordComplexity(password) - const key = Key.fromKeystore(JSON.stringify(wallet!.loadKeystore()), password) + const key = await Key.fromKeystore(JSON.stringify(wallet!.loadKeystore()), password) props.keystore = key.toKeystore(JSON.stringify(key.keysData!), newPassword) } else { throw new Error(i18n.t('messages.wallet-incorrect-password')) diff --git a/packages/neuron-wallet/src/decorators/errors.ts b/packages/neuron-wallet/src/decorators/errors.ts index 168184f504..6601ecba9f 100644 --- a/packages/neuron-wallet/src/decorators/errors.ts +++ b/packages/neuron-wallet/src/decorators/errors.ts @@ -1,12 +1,12 @@ -import { ResponseCode } from '../controllers' +import { ResponseCode } from '../utils/const' -export const CatchControllerError = (_target: any, _name: string, descripor: PropertyDescriptor) => { - const originalMethod = descripor.value +export const CatchControllerError = (_target: any, _name: string, descriptor: PropertyDescriptor) => { + const originalMethod = descriptor.value return { - ...descripor, + ...descriptor, async value(...args: any[]) { try { - return originalMethod(...args) + return await originalMethod(...args) } catch (err) { return { status: ResponseCode.Fail, diff --git a/packages/neuron-wallet/src/decorators/index.ts b/packages/neuron-wallet/src/decorators/index.ts index e51520df2d..55b5deea58 100644 --- a/packages/neuron-wallet/src/decorators/index.ts +++ b/packages/neuron-wallet/src/decorators/index.ts @@ -1,7 +1,10 @@ -import errorsDecorators from './errors' +import errorDecorators from './errors' +import validatorDecorators from './validators' -export const { CatchControllerError } = errorsDecorators +export const { CatchControllerError } = errorDecorators +export const { Validate, Password, Required } = validatorDecorators export default { - ...errorsDecorators, + ...errorDecorators, + ...validatorDecorators, } diff --git a/packages/neuron-wallet/src/decorators/validators.ts b/packages/neuron-wallet/src/decorators/validators.ts new file mode 100644 index 0000000000..9639b234bf --- /dev/null +++ b/packages/neuron-wallet/src/decorators/validators.ts @@ -0,0 +1,75 @@ +import 'reflect-metadata' + +import i18n from '../utils/i18n' +import { MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH } from '../utils/const' + +const requiredMetadataKey = Symbol('required') +const passwordMetadataKey = Symbol('password') + +export const verifyPasswordComplexity = (password: string) => { + if (password.length < MIN_PASSWORD_LENGTH) { + throw Error(i18n.t('messages.wallet-password-less-than-min-length', { minPasswordLength: MIN_PASSWORD_LENGTH })) + } + if (password.length > MAX_PASSWORD_LENGTH) { + throw Error(i18n.t('messages.wallet-password-more-than-max-length', { maxPasswordLength: MAX_PASSWORD_LENGTH })) + } + let complex = 0 + let reg = /\d/ + if (reg.test(password)) { + complex++ + } + reg = /[a-z]/ + if (reg.test(password)) { + complex++ + } + reg = /[A-Z]/ + if (reg.test(password)) { + complex++ + } + reg = /[^0-9a-zA-Z]/ + if (reg.test(password)) { + complex++ + } + if (complex < 3) { + throw Error(i18n.t('messages.wallet-password-letter-complexity')) + } +} + +const Required = (target: Object, propertyKey: string | symbol, index: number) => { + const indices: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [] + indices.push(index) + Reflect.defineMetadata(requiredMetadataKey, indices, target, propertyKey) +} + +const Password = (target: Object, propertyKey: string | symbol, parameterIndex: number) => { + Reflect.defineMetadata(passwordMetadataKey, parameterIndex, target, propertyKey) +} + +const Validate = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { + const originalMethod = descriptor.value + return { + ...descriptor, + async value(...args: any[]) { + const requiredIndices: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) + if (requiredIndices) { + requiredIndices.forEach(idx => { + if (idx >= args.length || args[idx] === undefined) { + throw new Error('Missing required argument') + } + }) + } + + const passwordIndex: number = Reflect.getOwnMetadata(passwordMetadataKey, target, propertyKey) + if (passwordIndex !== undefined) { + verifyPasswordComplexity(args[passwordIndex]) + } + return originalMethod(...args) + }, + } +} + +export default { + Required, + Password, + Validate, +} diff --git a/packages/neuron-wallet/src/keys/key.ts b/packages/neuron-wallet/src/keys/key.ts index 2e2744ddf1..ee2161fdc4 100644 --- a/packages/neuron-wallet/src/keys/key.ts +++ b/packages/neuron-wallet/src/keys/key.ts @@ -5,9 +5,8 @@ import SHA3 from 'sha3' import { v4 as uuid } from 'uuid' import Address, { HDAddress } from '../services/addresses' import { Keystore, KdfParams, KeysData } from './keystore' -import i18n from '../utils/i18n' -import { verifyPasswordComplexity } from '../utils/validators' import { Keychain } from './hd' +import { Validate, Required, Password } from '../decorators' export interface Addresses { receiving: HDAddress[] @@ -49,22 +48,17 @@ export default class Key { return bip39.generateMnemonic() } - static fromKeystore( - keystore: string, - password: string, + @Validate + static async fromKeystore( + @Required keystore: string, + @Password password: string, receivingAddressNumber = DefaultAddressNumber.Receiving, changeAddressNumber = DefaultAddressNumber.Change, ) { - if (!password) throw new Error(i18n.t('messages.password-is-required')) - verifyPasswordComplexity(password) - - if (!keystore) throw new Error(i18n.t('messages.keystore-is-required')) const keystoreObject: Keystore = JSON.parse(keystore) const key = new Key() key.keystore = keystoreObject - if (password === undefined) { - throw new Error('No password given.') - } else if (!key.checkPassword(password)) { + if (!key.checkPassword(password)) { throw new Error('Password error.') } const { kdfparams } = keystoreObject.crypto @@ -98,16 +92,13 @@ export default class Key { return key } + @Validate public static async fromMnemonic( - mnemonic: string, - password: string, + @Required mnemonic: string, + @Password password: string, receivingAddressNumber = DefaultAddressNumber.Receiving, changeAddressNumber = DefaultAddressNumber.Change, ) { - if (!password) throw new Error(i18n.t('messages.password-is-required')) - verifyPasswordComplexity(password) - - if (!mnemonic) throw new Error(i18n.t('messages.mnemonic-is-required')) if (!bip39.validateMnemonic(mnemonic)) { throw new Error('Wrong Mnemonic') } diff --git a/packages/neuron-wallet/src/services/networks.ts b/packages/neuron-wallet/src/services/networks.ts index 3c5023ed15..178cf75171 100644 --- a/packages/neuron-wallet/src/services/networks.ts +++ b/packages/neuron-wallet/src/services/networks.ts @@ -5,9 +5,8 @@ import Store from '../utils/store' import env from '../env' import windowManage from '../utils/windowManage' -import { ResponseCode } from '../controllers' import { NetworksMethod } from '../controllers/networks' -import { Channel } from '../utils/const' +import { Channel, ResponseCode } from '../utils/const' import nodeService from '../startup/nodeService' export type NetworkID = string diff --git a/packages/neuron-wallet/src/startup/initWindow.ts b/packages/neuron-wallet/src/startup/initWindow.ts index 793756547a..1f121fa8c8 100644 --- a/packages/neuron-wallet/src/startup/initWindow.ts +++ b/packages/neuron-wallet/src/startup/initWindow.ts @@ -1,6 +1,6 @@ import { app, BrowserWindow } from 'electron' -import controllers, { ResponseCode } from '../controllers' -import { Channel } from '../utils/const' +import controllers from '../controllers' +import { Channel, ResponseCode } from '../utils/const' const { WalletsController, NetworksController } = controllers diff --git a/packages/neuron-wallet/src/utils/const.ts b/packages/neuron-wallet/src/utils/const.ts index e97dc63fd3..eee33dc1e9 100644 --- a/packages/neuron-wallet/src/utils/const.ts +++ b/packages/neuron-wallet/src/utils/const.ts @@ -12,6 +12,12 @@ export enum Channel { Helpers = 'helpers', } +export enum ResponseCode { + Fail, + Success, +} + export default { Channel, + ResponseCode, } diff --git a/packages/neuron-wallet/src/utils/validators.ts b/packages/neuron-wallet/src/utils/validators.ts deleted file mode 100644 index 72bb46f9d2..0000000000 --- a/packages/neuron-wallet/src/utils/validators.ts +++ /dev/null @@ -1,35 +0,0 @@ -import i18n from './i18n' -import { MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH } from './const' - -export const verifyPasswordComplexity = (password: string) => { - if (password.length < MIN_PASSWORD_LENGTH) { - throw Error(i18n.t('messages.wallet-password-less-than-min-length', { minPasswordLength: MIN_PASSWORD_LENGTH })) - } - if (password.length > MAX_PASSWORD_LENGTH) { - throw Error(i18n.t('messages.wallet-password-more-than-max-length', { maxPasswordLength: MAX_PASSWORD_LENGTH })) - } - let complex = 0 - let reg = /\d/ - if (reg.test(password)) { - complex++ - } - reg = /[a-z]/ - if (reg.test(password)) { - complex++ - } - reg = /[A-Z]/ - if (reg.test(password)) { - complex++ - } - reg = /[^0-9a-zA-Z]/ - if (reg.test(password)) { - complex++ - } - if (complex < 3) { - throw Error(i18n.t('messages.wallet-password-letter-complexity')) - } -} - -export default { - verifyPasswordComplexity, -} diff --git a/packages/neuron-wallet/tests/keys/key.test.ts b/packages/neuron-wallet/tests/keys/key.test.ts index 682a5c6efc..86e6951b27 100644 --- a/packages/neuron-wallet/tests/keys/key.test.ts +++ b/packages/neuron-wallet/tests/keys/key.test.ts @@ -15,7 +15,7 @@ describe('Key tests', () => { }) it('import key from keystore', async () => { - const key = Key.fromKeystore(keystoreJson, '1qaz.2wsx', 20, 10) + const key = await Key.fromKeystore(keystoreJson, '1qaz.2wsx', 20, 10) expect(privateKey).toBe(key.keysData!.privateKey) expect(key.addresses!.receiving.length).toEqual(20) expect(key.addresses!.change.length).toEqual(10) diff --git a/packages/neuron-wallet/tests/utils/validators.test.ts b/packages/neuron-wallet/tests/utils/validators.test.ts index a6721ee27e..e8879d8503 100644 --- a/packages/neuron-wallet/tests/utils/validators.test.ts +++ b/packages/neuron-wallet/tests/utils/validators.test.ts @@ -1,4 +1,4 @@ -import { verifyPasswordComplexity } from '../../src/utils/validators' +import { verifyPasswordComplexity } from '../../src/decorators/validators' import i18n from '../../src/utils/i18n' import { MIN_PASSWORD_LENGTH } from '../../src/utils/const'