Account change password functionality (#332)
* Account change password * Small cleanups * Props, State to Component
This commit is contained in:
parent
0720454772
commit
ad4e976f54
164
packages/app-accounts/src/ChangePass.tsx
Normal file
164
packages/app-accounts/src/ChangePass.tsx
Normal file
@ -0,0 +1,164 @@
|
||||
// Copyright 2017-2018 @polkadot/app-accounts authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { KeyringPair } from '@polkadot/util-keyring/types';
|
||||
import { I18nProps } from '@polkadot/ui-app/types';
|
||||
|
||||
import React from 'react';
|
||||
import Button from '@polkadot/ui-app/Button';
|
||||
import Modal from '@polkadot/ui-app/Modal';
|
||||
import Password from '@polkadot/ui-app/Password';
|
||||
import AddressSummary from '@polkadot/ui-app/AddressSummary';
|
||||
import keyring from '@polkadot/ui-keyring/index';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
account: KeyringPair,
|
||||
onClose: () => void
|
||||
};
|
||||
|
||||
type State = {
|
||||
isNewValid: boolean,
|
||||
isOldValid: boolean,
|
||||
newPass: string,
|
||||
oldPass: string
|
||||
};
|
||||
|
||||
class ChangePass extends React.PureComponent<Props, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isNewValid: false,
|
||||
isOldValid: false,
|
||||
newPass: '',
|
||||
oldPass: ''
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<Modal
|
||||
size='tiny'
|
||||
dimmer='inverted'
|
||||
open
|
||||
>
|
||||
{this.renderContent()}
|
||||
{this.renderButtons()}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
private renderButtons () {
|
||||
const { onClose, t } = this.props;
|
||||
const { isNewValid, isOldValid } = this.state;
|
||||
|
||||
return (
|
||||
<Modal.Actions>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={onClose}
|
||||
text={t('change.close', {
|
||||
defaultValue: 'Cancel'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isDisabled={!isNewValid || !isOldValid}
|
||||
isPrimary
|
||||
onClick={this.doChange}
|
||||
text={t('change.change', {
|
||||
defaultValue: 'Change'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
</Modal.Actions>
|
||||
);
|
||||
}
|
||||
|
||||
private renderContent () {
|
||||
const { account, t } = this.props;
|
||||
const { isNewValid, isOldValid, newPass, oldPass } = this.state;
|
||||
|
||||
return [
|
||||
<Modal.Header key='header'>
|
||||
{t('change.header', {
|
||||
defaultValue: 'Change account password'
|
||||
})}
|
||||
</Modal.Header>,
|
||||
<Modal.Content key='content'>
|
||||
<AddressSummary
|
||||
className='accounts--Modal-Address'
|
||||
value={account.address()}
|
||||
/>
|
||||
<div className='ui--row'>
|
||||
<Password
|
||||
autoFocus
|
||||
isError={!isOldValid}
|
||||
label={t('change.old', { defaultValue: 'old password' })}
|
||||
onChange={this.onChangeOld}
|
||||
tabIndex={1}
|
||||
value={oldPass}
|
||||
/>
|
||||
<Password
|
||||
isError={!isNewValid}
|
||||
label={t('change.new', { defaultValue: 'new password' })}
|
||||
onChange={this.onChangeNew}
|
||||
tabIndex={2}
|
||||
value={newPass}
|
||||
/>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
];
|
||||
}
|
||||
|
||||
private doChange = (): void => {
|
||||
const { account, onClose } = this.props;
|
||||
const { newPass, oldPass } = this.state;
|
||||
|
||||
try {
|
||||
if (!account.isLocked()) {
|
||||
account.lock();
|
||||
}
|
||||
|
||||
account.decodePkcs8(oldPass);
|
||||
} catch (error) {
|
||||
this.setState({ isOldValid: false });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
keyring.encryptAccount(account, newPass);
|
||||
} catch (error) {
|
||||
this.setState({ isNewValid: false });
|
||||
return;
|
||||
}
|
||||
|
||||
onClose();
|
||||
}
|
||||
|
||||
private onChangeNew = (newPass: string) => {
|
||||
this.setState({
|
||||
isNewValid: this.validatePass(newPass),
|
||||
newPass
|
||||
});
|
||||
}
|
||||
|
||||
private onChangeOld = (oldPass: string) => {
|
||||
this.setState({
|
||||
isOldValid: this.validatePass(oldPass),
|
||||
oldPass
|
||||
});
|
||||
}
|
||||
|
||||
private validatePass (password: string): boolean {
|
||||
return password.length > 0 && password.length <= 32;
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(ChangePass);
|
||||
@ -12,6 +12,7 @@ import Input from '@polkadot/ui-app/Input';
|
||||
import InputAddress from '@polkadot/ui-app/InputAddress';
|
||||
import keyring from '@polkadot/ui-keyring/index';
|
||||
|
||||
import ChangePass from './ChangePass';
|
||||
import Forgetting from './Forgetting';
|
||||
import translate from './translate';
|
||||
|
||||
@ -24,7 +25,8 @@ type State = {
|
||||
current: KeyringPair | null,
|
||||
editedName: string,
|
||||
isEdited: boolean,
|
||||
isForgetOpen: boolean
|
||||
isForgetOpen: boolean,
|
||||
isPasswordOpen: boolean
|
||||
};
|
||||
|
||||
class Editor extends React.PureComponent<Props, State> {
|
||||
@ -64,6 +66,14 @@ class Editor extends React.PureComponent<Props, State> {
|
||||
})}
|
||||
/>
|
||||
<Button.Group.Divider />
|
||||
<Button
|
||||
isDisabled={isEdited}
|
||||
onClick={this.togglePass}
|
||||
text={t('editor.changePass', {
|
||||
defaultValue: 'Change Password'
|
||||
})}
|
||||
/>
|
||||
<Button.Group.Divider />
|
||||
<Button
|
||||
isDisabled={!isEdited}
|
||||
onClick={this.onDiscard}
|
||||
@ -129,7 +139,7 @@ class Editor extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
renderModals () {
|
||||
const { current, isForgetOpen } = this.state;
|
||||
const { current, isForgetOpen, isPasswordOpen } = this.state;
|
||||
|
||||
if (!current) {
|
||||
return null;
|
||||
@ -149,6 +159,16 @@ class Editor extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
if (isPasswordOpen) {
|
||||
modals.push(
|
||||
<ChangePass
|
||||
account={current}
|
||||
key='modal-change-pass'
|
||||
onClose={this.togglePass}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return modals;
|
||||
}
|
||||
|
||||
@ -159,7 +179,8 @@ class Editor extends React.PureComponent<Props, State> {
|
||||
? current.getMeta().name || ''
|
||||
: '',
|
||||
isEdited: false,
|
||||
isForgetOpen: false
|
||||
isForgetOpen: false,
|
||||
isPasswordOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
@ -184,7 +205,8 @@ class Editor extends React.PureComponent<Props, State> {
|
||||
current,
|
||||
editedName,
|
||||
isEdited,
|
||||
isForgetOpen: false
|
||||
isForgetOpen: false,
|
||||
isPasswordOpen: false
|
||||
};
|
||||
}
|
||||
);
|
||||
@ -239,6 +261,23 @@ class Editor extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
togglePass = (): void => {
|
||||
this.setState(
|
||||
({ current, isPasswordOpen }: State) => {
|
||||
if (!current) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// NOTE We re-get the account from the keyring, if changed it will load the
|
||||
// new instance (this is not quite obvious...)
|
||||
return {
|
||||
current: keyring.getPair(current.publicKey()),
|
||||
isPasswordOpen: !isPasswordOpen
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onForget = (): void => {
|
||||
const { current } = this.state;
|
||||
|
||||
|
||||
16
packages/ui-keyring/src/account/encrypt.ts
Normal file
16
packages/ui-keyring/src/account/encrypt.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-keyring authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { KeyringPair } from '@polkadot/util-keyring/types';
|
||||
import { State } from '../types';
|
||||
|
||||
export default function encryptAccount (state: State, pair: KeyringPair, password: string): void {
|
||||
const { accounts, keyring } = state;
|
||||
const json = pair.toJson(password);
|
||||
|
||||
json.meta.whenEdited = Date.now();
|
||||
|
||||
keyring.addFromJson(json);
|
||||
accounts.add(json.address, json);
|
||||
}
|
||||
@ -15,6 +15,7 @@ import loadAll from './loadAll';
|
||||
import createAccount from './account/create';
|
||||
import forgetAccount from './account/forget';
|
||||
import isAvailable from './isAvailable';
|
||||
import encryptAccount from './account/encrypt';
|
||||
import saveAccount from './account/save';
|
||||
import saveAccountMeta from './account/meta';
|
||||
import forgetAddress from './address/forget';
|
||||
@ -35,6 +36,8 @@ loadAll(state);
|
||||
export default ({
|
||||
createAccount: (seed: Uint8Array, password?: string, meta?: KeyringPair$Meta): KeyringPair =>
|
||||
createAccount(state, seed, password, meta),
|
||||
encryptAccount: (pair: KeyringPair, password: string): void =>
|
||||
encryptAccount(state, pair, password),
|
||||
forgetAccount: (address: string): void =>
|
||||
forgetAccount(state, address),
|
||||
forgetAddress: (address: string): void =>
|
||||
|
||||
1
packages/ui-keyring/src/types.d.ts
vendored
1
packages/ui-keyring/src/types.d.ts
vendored
@ -36,6 +36,7 @@ export type State = {
|
||||
|
||||
export type KeyringInstance = {
|
||||
createAccount: (seed: Uint8Array, password?: string, meta?: KeyringPair$Meta) => KeyringPair,
|
||||
encryptAccount: (pair: KeyringPair, password: string) => void,
|
||||
forgetAccount: (address: string) => void,
|
||||
forgetAddress: (address: string) => void,
|
||||
getAccounts: () => Array<KeyringAddress>,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user