Account change password functionality (#332)

* Account change password

* Small cleanups

* Props, State to Component
This commit is contained in:
Jaco Greeff 2018-09-26 08:14:48 +02:00 committed by GitHub
parent 0720454772
commit ad4e976f54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 227 additions and 4 deletions

View 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);

View File

@ -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;

View 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);
}

View File

@ -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 =>

View File

@ -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>,