From 538844697810e892073e1d93be1e912333595706 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Sun, 12 Nov 2017 21:34:34 +0300 Subject: [PATCH 1/5] harden security, reset input fields --- .../importKeyModal/importKeyModal.js | 13 ++++ .../importKeyModal/importKeyModal.render.js | 11 ++-- .../settings/settings.exportKeysPanel.js | 45 +++++++++----- react/src/components/login/login.js | 60 +++++++++++++------ react/src/components/login/login.render.js | 4 ++ react/src/components/overrides.scss | 10 ++++ 6 files changed, 106 insertions(+), 37 deletions(-) diff --git a/react/src/components/dashboard/importKeyModal/importKeyModal.js b/react/src/components/dashboard/importKeyModal/importKeyModal.js index 5e130ce18..e3a87e779 100755 --- a/react/src/components/dashboard/importKeyModal/importKeyModal.js +++ b/react/src/components/dashboard/importKeyModal/importKeyModal.js @@ -187,6 +187,19 @@ class ImportKeyModal extends React.Component { ); } }); + + this.state({ + passphraseWif: null, + passphraseAddress: null, + wifkeysPassphrase: null, + wifkeysPassphraseTextarea: null, + importWithRescan: this.state.importWithRescan ? false : this.state.importWithRescan, + }); + + // reset input vals + this.refs.wif + this.refs.wifkeysPassphrase.value = ''; + this.refs.wifkeysPassphraseTextarea.value = ''; } generateKeysFromPassphrase() { diff --git a/react/src/components/dashboard/importKeyModal/importKeyModal.render.js b/react/src/components/dashboard/importKeyModal/importKeyModal.render.js index 053d9ce56..6fcfbdda9 100644 --- a/react/src/components/dashboard/importKeyModal/importKeyModal.render.js +++ b/react/src/components/dashboard/importKeyModal/importKeyModal.render.js @@ -26,23 +26,25 @@ export const ImportKeyModalRender = function() { { translate('IMPORT_KEY.NOTICE') }: { translate('IMPORT_KEY.NOTICE_DESC') }.  { translate('IMPORT_KEY.KMD_RESCAN_WARNING_TIME') }.

-
{ translate('IMPORT_KEY.SHOW_ADDRESS_AND_WIF') }
- + { this.state.passphraseAddress && this.state.passphraseWif &&
@@ -118,6 +120,7 @@ export const ImportKeyModalRender = function() { type="text" className="form-control" name="wif" + ref="wif" onChange={ this.updateInput } value={ this.state.wif } />
diff --git a/react/src/components/dashboard/settings/settings.exportKeysPanel.js b/react/src/components/dashboard/settings/settings.exportKeysPanel.js index e3c50be84..7b7cf168a 100644 --- a/react/src/components/dashboard/settings/settings.exportKeysPanel.js +++ b/react/src/components/dashboard/settings/settings.exportKeysPanel.js @@ -28,7 +28,12 @@ class ExportKeysPanel extends React.Component { props.Dashboard.activeSection !== 'settings') { this.setState(Object.assign({}, this.state, { keys: null, + wifkeysPassphrase: '', })); + + // reset input vals + this.refs.wifkeysPassphrase.value = ''; + this.refs.wifkeysPassphraseTextarea.value = ''; } } @@ -46,8 +51,12 @@ class ExportKeysPanel extends React.Component { } else { this.setState(Object.assign({}, this.state, { keys: keys.result, + wifkeysPassphrase: '', })); - console.warn(keys); + + // reset input vals + this.refs.wifkeysPassphrase.value = ''; + this.refs.wifkeysPassphraseTextarea.value = ''; } }) } @@ -162,22 +171,24 @@ class ExportKeysPanel extends React.Component {
-
@@ -191,25 +202,27 @@ class ExportKeysPanel extends React.Component {
- +
{ this.state.keys &&
- - - - - - { this.renderWifKeys() } +
- { translate('SETTINGS.ADDRESS_LIST') } - - { translate('SETTINGS.WIF_KEY_LIST') } -
+ + + + + + { this.renderWifKeys() } +
+ { translate('SETTINGS.ADDRESS_LIST') } + + { translate('SETTINGS.WIF_KEY_LIST') } +
diff --git a/react/src/components/login/login.js b/react/src/components/login/login.js index 55fc37ab5..e0e19691c 100644 --- a/react/src/components/login/login.js +++ b/react/src/components/login/login.js @@ -8,7 +8,8 @@ import { startInterval, getDexCoins, triggerToaster, - toggleLoginSettingsModal + toggleLoginSettingsModal, + stopInterval, } from '../../actions/actionCreators'; import Config from '../../config'; import Store from '../../store'; @@ -197,6 +198,7 @@ class Login extends React.Component { if (props.Login.pinList === 'no pins') { props.Login.pinList = []; } + if (props && props.Main && props.Main.isLoggedIn) { @@ -215,21 +217,48 @@ class Login extends React.Component { if (props && props.Main && !props.Main.isLoggedIn) { + document.body.className = 'page-login layout-full page-dark'; + + if (props.Interval && + props.Interval.interval && + props.Interval.interval.sync) { + Store.dispatch( + stopInterval( + 'sync', + props.Interval.interval + ) + ); + } + this.setState({ display: true, activeLoginSection: this.state.activeLoginSection !== 'signup' ? 'login' : 'signup', }); + } + if (props.Main && + props.Main.total === 0) { document.body.className = 'page-login layout-full page-dark'; + + if (props.Interval && + props.Interval.interval && + props.Interval.interval.sync) { + Store.dispatch( + stopInterval( + 'sync', + props.Interval.interval + ) + ); + } } if (this.state.activeLoginSection !== 'signup' && props && props.Main && props.Main.isLoggedIn) { - this.setState({ - activeLoginSection: 'activateCoin', - }); + this.setState({ + activeLoginSection: 'activateCoin', + }); } } @@ -286,12 +315,6 @@ class Login extends React.Component { } loginSeed() { - // reset the login pass phrase values so that when the user logs out, the values are clear - this.setState({ - loginPassphrase: null, - loginPassPhraseSeedType: null, - }); - if (this.state.shouldEncryptSeed) { Store.dispatch(encryptPassphrase(this.state.loginPassphrase, this.state.encryptKey, this.state.pubKey)); } @@ -306,6 +329,16 @@ class Login extends React.Component { shepherdElectrumCoins() ); } + + // reset the login pass phrase values so that when the user logs out, the values are clear + this.setState({ + loginPassphrase: '', + loginPassPhraseSeedType: null, + }); + + // reset login input vals + this.refs.loginPassphrase.value = ''; + this.refs.loginPassphraseEdit.value = ''; } loadPinList() { @@ -362,13 +395,6 @@ class Login extends React.Component { } execWalletCreate() { - /*Store.dispatch( - createNewWallet( - this.state.randomSeedConfirm, - this.props.Dashboard.activeHandle - ) - );*/ - Store.dispatch( shepherdElectrumAuth(this.state.randomSeedConfirm) ); diff --git a/react/src/components/login/login.render.js b/react/src/components/login/login.render.js index c3f97f250..bf919b4d6 100644 --- a/react/src/components/login/login.render.js +++ b/react/src/components/login/login.render.js @@ -55,13 +55,17 @@ const LoginRender = function () { type="password" className={ !this.state.seedInputVisibility ? 'form-control' : 'hide' } name="loginPassphrase" + ref="loginPassphraseEdit" onChange={ this.updateLoginPassPhraseInput } onKeyDown={ (event) => this.handleKeydown(event) } + autoComplete="off" value={ this.state.loginPassphrase || '' } /> diff --git a/react/src/components/overrides.scss b/react/src/components/overrides.scss index 8253a30cf..d5365690c 100644 --- a/react/src/components/overrides.scss +++ b/react/src/components/overrides.scss @@ -511,4 +511,14 @@ select{ .coind-remove-icon { transform: rotate(45deg); top: 45px; +} + +.coind-remove-icon-spv { + top: 19px; +} + +.no-borders { + tbody > tr > td { + border: none; + } } \ No newline at end of file From 4937f1764d4a07613b64ac3a1bd6c62b83c77697 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Sun, 12 Nov 2017 23:58:13 +0300 Subject: [PATCH 2/5] header coin name fix --- .../src/components/dashboard/walletsMain/walletsMain.render.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/react/src/components/dashboard/walletsMain/walletsMain.render.js b/react/src/components/dashboard/walletsMain/walletsMain.render.js index b14303a32..3e4a1a526 100644 --- a/react/src/components/dashboard/walletsMain/walletsMain.render.js +++ b/react/src/components/dashboard/walletsMain/walletsMain.render.js @@ -5,6 +5,7 @@ import SendCoin from '../sendCoin/sendCoin'; import WalletsProgress from '../walletsProgress/walletsProgress'; import WalletsData from '../walletsData/walletsData'; import ReceiveCoin from '../receiveCoin/receiveCoin'; +import { getCoinTitle } from '../../../util/coinHelper'; const WalletsMainRender = function() { return ( @@ -22,7 +23,7 @@ const WalletsMainRender = function() { - { this.props.ActiveCoin.coin } + { getCoinTitle(this.props.ActiveCoin.coin).name } From 277d3fd02180310fdff8632efa2ce3a51e8c1e1c Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 13 Nov 2017 01:36:38 +0300 Subject: [PATCH 3/5] spv lock, logout --- .../src/components/dashboard/navbar/navbar.js | 25 +++++++++++++++++++ .../dashboard/navbar/navbar.render.js | 16 ++++++++++++ 2 files changed, 41 insertions(+) diff --git a/react/src/components/dashboard/navbar/navbar.js b/react/src/components/dashboard/navbar/navbar.js index a5e9042ea..ec62a38bb 100755 --- a/react/src/components/dashboard/navbar/navbar.js +++ b/react/src/components/dashboard/navbar/navbar.js @@ -6,6 +6,10 @@ import { stopInterval, startInterval, displayImportKeyModal, + shepherdElectrumLock, + shepherdElectrumLogout, + getDexCoins, + activeHandle, } from '../../../actions/actionCreators'; import Store from '../../../store'; import Config from '../../../config'; @@ -23,6 +27,24 @@ class Navbar extends React.Component { this.openDropMenu = this.openDropMenu.bind(this); this.handleClickOutside = this.handleClickOutside.bind(this); this._checkAC = this._checkAC.bind(this); + this.spvLock = this.spvLock.bind(this); + this.spvLogout = this.spvLogout.bind(this); + } + + spvLock() { + shepherdElectrumLock() + .then((res) => { + Store.dispatch(getDexCoins()); + Store.dispatch(activeHandle()); + }); + } + + spvLogout() { + shepherdElectrumLogout() + .then((res) => { + Store.dispatch(getDexCoins()); + Store.dispatch(activeHandle()); + }); } componentWillMount() { @@ -106,6 +128,9 @@ const mapStateToProps = (state) => { Interval: { interval: state.Interval.interval, }, + Main: { + isLoggedIn: state.Main.isLoggedIn, + }, }; }; diff --git a/react/src/components/dashboard/navbar/navbar.render.js b/react/src/components/dashboard/navbar/navbar.render.js index fca2a46fa..6a8bb05cc 100644 --- a/react/src/components/dashboard/navbar/navbar.render.js +++ b/react/src/components/dashboard/navbar/navbar.render.js @@ -109,6 +109,22 @@ const NavbarRender = function() { { translate('ABOUT.ABOUT_AGAMA') } + { this.props.Main && + this.props.Main.isLoggedIn && +
  • + + Lock + +
  • + } + { this.props.Main && + this.props.Main.isLoggedIn && +
  • + + Logout + +
  • + } From c48bd079e9a37d8fe9835b402220775d0a191b79 Mon Sep 17 00:00:00 2001 From: pbca26 Date: Mon, 13 Nov 2017 01:37:04 +0300 Subject: [PATCH 4/5] remove coin extended --- react/src/actions/actions/addCoin.js | 2 +- react/src/actions/actions/coinList.js | 57 ++++++++++++++++++- react/src/components/addcoin/addcoin.js | 2 +- .../claimInterestModal/claimInterestModal.js | 4 +- .../dashboard/coinTile/coinTileItem.js | 32 +++++++++-- .../dashboard/coinTile/coinTileItem.render.js | 22 ++----- .../importKeyModal/importKeyModal.js | 4 +- .../importKeyModal/importKeyModal.render.js | 2 +- .../settings/settings.exportKeysPanel.js | 6 +- react/src/components/login/login.js | 28 +++++---- react/src/components/login/login.render.js | 6 +- react/src/components/overrides.scss | 1 + 12 files changed, 118 insertions(+), 48 deletions(-) diff --git a/react/src/actions/actions/addCoin.js b/react/src/actions/actions/addCoin.js index ab2e2181d..642631db7 100644 --- a/react/src/actions/actions/addCoin.js +++ b/react/src/actions/actions/addCoin.js @@ -243,7 +243,7 @@ export function shepherdHerd(coin, mode, path, startupParams) { console.warn(acData); dispatch( triggerToaster( - `Error starting ${coin} daemon. Port ${acData.rpc} is already taken!`, // translate + translate('TOASTR.ERROR_STARTING_DAEMON', coin) + ' ' + translate('TOASTR.PORT_IS_TAKEN', acData.rpc), translate('TOASTR.SERVICE_NOTIFICATION'), 'error', false diff --git a/react/src/actions/actions/coinList.js b/react/src/actions/actions/coinList.js index c078fb0ba..7a69e197e 100644 --- a/react/src/actions/actions/coinList.js +++ b/react/src/actions/actions/coinList.js @@ -1,6 +1,54 @@ import { triggerToaster } from '../actionCreators'; import Config from '../../config'; +export function shepherdElectrumLock() { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/lock`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: '', + }) + .catch((error) => { + console.log(error); + dispatch( + triggerToaster( + 'shepherdElectrumLock', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => resolve(json)) + }); +} + +export function shepherdElectrumLogout() { + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/logout`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: '', + }) + .catch((error) => { + console.log(error); + dispatch( + triggerToaster( + 'shepherdElectrumLogout', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => resolve(json)) + }); +} + export function shepherdStopCoind(coin) { return new Promise((resolve, reject) => { fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/coind/stop`, { @@ -25,14 +73,19 @@ export function shepherdStopCoind(coin) { }); } -export function shepherdRemoveCoin(coin) { +export function shepherdRemoveCoin(coin, mode) { return new Promise((resolve, reject) => { fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/coins/remove`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: coin === 'KMD' ? '' : JSON.stringify({ chain: coin }), + body: JSON.stringify(coin === 'KMD' && mode === 'native' ? { + mode, + } : { + mode, + chain: coin, + }), }) .catch((error) => { console.log(error); diff --git a/react/src/components/addcoin/addcoin.js b/react/src/components/addcoin/addcoin.js index 56e9f57d3..43237f64c 100644 --- a/react/src/components/addcoin/addcoin.js +++ b/react/src/components/addcoin/addcoin.js @@ -128,7 +128,7 @@ class AddCoin extends React.Component { addCoinProps.display !== this.state.display) { this.setState(Object.assign({}, this.state, { display: addCoinProps.display, - modalClassName: addCoinProps.display ? 'show fade' : 'show fade', + modalClassName: 'show fade', })); setTimeout(() => { diff --git a/react/src/components/dashboard/claimInterestModal/claimInterestModal.js b/react/src/components/dashboard/claimInterestModal/claimInterestModal.js index 13cff7858..8fe21d3ea 100755 --- a/react/src/components/dashboard/claimInterestModal/claimInterestModal.js +++ b/react/src/components/dashboard/claimInterestModal/claimInterestModal.js @@ -35,7 +35,9 @@ class ClaimInterestModal extends React.Component { } componentWillMount() { - this.loadListUnspent(); + if (this.props.ActiveCoin.mode === 'native') { + this.loadListUnspent(); + } } loadListUnspent() { diff --git a/react/src/components/dashboard/coinTile/coinTileItem.js b/react/src/components/dashboard/coinTile/coinTileItem.js index e3d3e4493..cbd3c6419 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.js @@ -30,7 +30,7 @@ import Config from '../../../config'; import CoinTileItemRender from './coinTileItem.render'; -const SPV_DASHBOARD_UPDATE_TIMEOUT = 60000; +const SPV_DASHBOARD_UPDATE_TIMEOUT = 10000; const ACTIVE_HANDLE_TIMEOUT_COIND_NATIVE = 15000; const COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD = window.require('electron').remote.getCurrentWindow().appConfig.failedRPCAttemptsThreshold || 10; @@ -43,6 +43,28 @@ class CoinTileItem extends React.Component { this.autoSetActiveCoin = this.autoSetActiveCoin.bind(this); } + renderStopCoinButton() { + if (this.props.Main && + this.props.Main.coins && + this.props.Main.coins.native && + this.props.Main.coins.native.length) { + return true; + } + } + + renderRemoveCoinButton() { + if (this.props.Main && + this.props.Main.coins && + ((this.props.Main.coins.native && + this.props.Main.coins.native.length && + this.state.appConfig && + !this.state.appConfig.stopNativeDaemonsOnQuit) || + (this.props.Main.coins.spv && + this.props.Main.coins.spv.length))) { + return true; + } + } + autoSetActiveCoin() { const modes = [ 'native', @@ -97,8 +119,8 @@ class CoinTileItem extends React.Component { }); } - removeCoin(coin) { - shepherdRemoveCoin(coin) + removeCoin(coin, mode) { + shepherdRemoveCoin(coin, mode) .then((res) => { Store.dispatch( triggerToaster( @@ -189,9 +211,7 @@ class CoinTileItem extends React.Component { ) ); } - } - - if (mode === 'spv') { + } else if (mode === 'spv') { Store.dispatch(shepherdElectrumBalance(coin, this.props.Dashboard.electrumCoins[coin].pub)); Store.dispatch(shepherdElectrumTransactions(coin, this.props.Dashboard.electrumCoins[coin].pub)); } diff --git a/react/src/components/dashboard/coinTile/coinTileItem.render.js b/react/src/components/dashboard/coinTile/coinTileItem.render.js index f7b111708..08578f0e6 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.render.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.render.js @@ -24,29 +24,17 @@ const CoinTileItemRender = function() { - { item.mode === 'native' && - this.props.Main && - this.props.Main.coins && - this.props.Main.coins.native && - this.props.Main.coins.native.length && - this.props.Main.coins.native.length > 1 && + { this.renderStopCoinButton() && this.stopCoind(item.coin) } + onClick={ () => this.stopCoind(item.coin, item.mode) } title="Stop" className="icon fa-stop-circle coind-stop-icon"> } - { item.mode === 'native' && - this.props.Main && - this.props.Main.coins && - this.props.Main.coins.native && - this.props.Main.coins.native.length && - this.props.Main.coins.native.length > 1 && - this.state.appConfig && - !this.state.appConfig.stopNativeDaemonsOnQuit && + { this.renderRemoveCoinButton() && this.removeCoin(item.coin) } + onClick={ () => this.removeCoin(item.coin, item.mode) } title="Remove" - className="icon fa-plus-circle coind-remove-icon"> + className={ 'icon fa-plus-circle ' + (item.mode === 'spv' ? 'coind-remove-icon coind-remove-icon-spv' : 'coind-remove-icon') }>
    } { this.props.Dashboard && this.props.Dashboard.electrumCoins && diff --git a/react/src/components/dashboard/importKeyModal/importKeyModal.js b/react/src/components/dashboard/importKeyModal/importKeyModal.js index e3a87e779..824f4b41d 100755 --- a/react/src/components/dashboard/importKeyModal/importKeyModal.js +++ b/react/src/components/dashboard/importKeyModal/importKeyModal.js @@ -77,11 +77,11 @@ class ImportKeyModal extends React.Component { this.setState({ trimPassphraseTimer: _trimPassphraseTimer, - [e.target.name]: newValue, + [e.target.name === 'wifkeysPassphraseTextarea' ? 'wifkeysPassphrase' : e.target.name]: newValue, }); } else { this.setState({ - [e.target.name]: e.target.value, + [e.target.name === 'wifkeysPassphraseTextarea' ? 'wifkeysPassphrase' : e.target.name]: e.target.value, }); } } diff --git a/react/src/components/dashboard/importKeyModal/importKeyModal.render.js b/react/src/components/dashboard/importKeyModal/importKeyModal.render.js index 6fcfbdda9..a13065781 100644 --- a/react/src/components/dashboard/importKeyModal/importKeyModal.render.js +++ b/react/src/components/dashboard/importKeyModal/importKeyModal.render.js @@ -43,7 +43,7 @@ export const ImportKeyModalRender = function() { autoComplete="off" className={ this.state.seedInputVisibility ? 'form-control' : 'hide' } id="wifkeysPassphraseTextarea" - name="wifkeysPassphrase" + name="wifkeysPassphraseTextarea" ref="wifkeysPassphraseTextarea" onChange={ this.updateInput } value={ this.state.wifkeysPassphrase }> diff --git a/react/src/components/dashboard/settings/settings.exportKeysPanel.js b/react/src/components/dashboard/settings/settings.exportKeysPanel.js index 7b7cf168a..6fcada6a4 100644 --- a/react/src/components/dashboard/settings/settings.exportKeysPanel.js +++ b/react/src/components/dashboard/settings/settings.exportKeysPanel.js @@ -125,11 +125,11 @@ class ExportKeysPanel extends React.Component { this.setState({ trimPassphraseTimer: _trimPassphraseTimer, - [e.target.name]: newValue, + [e.target.name === 'wifkeysPassphraseTextarea' ? 'wifkeysPassphrase' : e.target.name]: newValue, }); } else { this.setState({ - [e.target.name]: e.target.value, + [e.target.name === 'wifkeysPassphraseTextarea' ? 'wifkeysPassphrase' : e.target.name]: e.target.value, }); } } @@ -189,7 +189,7 @@ class ExportKeysPanel extends React.Component { autoComplete="off" id="wifkeysPassphraseTextarea" ref="wifkeysPassphraseTextarea" - name="wifkeysPassphrase" + name="wifkeysPassphraseTextarea" onChange={ this.updateInput } value={ this.state.wifkeysPassphrase }> this.handleKeydown(event) } autoComplete="off" @@ -63,8 +63,8 @@ const LoginRender = function () {