Filter request popups (#5843)

* Formatting

* Update dapps to use web3Provider

* Remove secure flag from config

* Basic request approve/reject popup

* Lenient provider checks

* Case sensitivity fix
This commit is contained in:
Jaco Greeff 2017-06-14 17:12:13 +02:00 committed by GitHub
parent 38b4ba8d33
commit 58f15f1674
35 changed files with 338 additions and 135 deletions

View File

@ -30,11 +30,7 @@ export default class Api extends EventEmitter {
constructor (provider, allowSubscriptions = true) {
super();
if (!provider || (!isFunction(provider.send) && !isFunction(provider.execute))) {
throw new Error('Api needs provider with send() function');
}
if (!isFunction(provider.send)) {
if (!provider || !isFunction(provider.send)) {
console.warn(new Error('deprecated: Api needs provider with send() function, old-style Transport found instead'));
}

View File

@ -22,18 +22,6 @@ import util from './util';
import Api from './api';
describe('api/Api', () => {
describe('constructor', () => {
it('requires defined/non-null provider object', () => {
expect(() => new Api()).to.throw(/Api needs provider/);
expect(() => new Api(null)).to.throw(/Api needs provider/);
});
it('requires an send function on the transport object', () => {
expect(() => new Api({})).to.throw(/Api needs provider/);
expect(() => new Api({ send: true })).to.throw(/Api needs provider/);
});
});
describe('interface', () => {
const api = new Api(new Api.Provider.Http(TEST_HTTP_URL, -1));

View File

@ -46,7 +46,11 @@ export default class PostMessage {
return;
}
this._callbacks[id](error, result);
if (error) {
console.error(from, error);
}
this._callbacks[id](error && new Error(error), result);
this._callbacks[id] = null;
}
}

View File

@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const api = window.parity.api;
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
export {
api

View File

@ -15,8 +15,11 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Web3 from 'web3';
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
const api = window.parent.secureApi;
let web3;
api.parity.dappsUrl().then(url => {

View File

@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const api = window.parent.secureApi;
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
function trackRequest (signerRequestId, statusCallback) {
return api.pollMethod('parity_checkRequest', signerRequestId)

View File

@ -22,7 +22,6 @@
}
.container {
font-family: 'Roboto';
vertical-align: middle;
padding: 4em 0;
margin: 0 0 2em 0;

View File

@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const api = window.parent.secureApi;
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
export {
api

View File

@ -12,7 +12,7 @@
margin: 0;
padding: 0;
background: white;
font-family: 'Roboto', sans-serif;
font-family: 'Roboto', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Geneva, Arial, sans-serif;
font-size: 16px;
font-weight: 300;
}
@ -30,8 +30,5 @@
<div class="loading">Loading</div>
</div>
<script src="vendor.js"></script>
<% if (!htmlWebpackPlugin.options.secure) { %>
<script src="/parity-utils/parity.js"></script>
<% } %>
</body>
</html>

View File

@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const api = window.parent.secureApi;
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
export {
api

View File

@ -14,8 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const api = window.parity.api;
const { bytesToHex, sha3, toWei, fromWei } = window.parity.api.util;
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
const { bytesToHex, sha3, toWei, fromWei } = api.util;
export {
api,

View File

@ -18,7 +18,6 @@
.container {
background: black;
color: #eee;
font-family: 'Roboto';
vertical-align: middle;
}

View File

@ -79,7 +79,7 @@
padding: 1em;
color: #eee;
font-size: 0.75em;
font-family: 'Roboto Mono';
font-family: 'Roboto Mono', monospace;
width: 100%;
border: none;
box-sizing: border-box;

View File

@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const { api } = window.parity;
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
export {
api

View File

@ -21,7 +21,7 @@
border: 0;
color: #333;
font-size: 16px;
font-family: 'Roboto', sans-serif;
font-family: 'Roboto', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Geneva, Arial, sans-serif;
font-weight: 300;
margin: 0;
padding: 0;

View File

@ -17,7 +17,6 @@
.container {
color: #444;
font-family: 'Roboto';
vertical-align: middle;
min-height: 100vh;
position:relative;

View File

@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const { api } = window.parity;
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
export {
api

View File

@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const api = window.parent.secureApi;
import Api from '@parity/api';
const web3Provider = (window.parity && window.parity.web3Provider) || (window.parent && window.parent.web3Provider);
const api = new Api(web3Provider);
export {
api

View File

@ -16,8 +16,7 @@
"version": "0.3",
"author": "Gav Wood <gavin@parity.io>",
"position": "top-right",
"visible": true,
"secure": true
"visible": true
},
{
"id": "0xf9f2d620c2e08f83e45555247146c62185e4ab7cf82a4b9002a265a0d020348f",
@ -44,8 +43,7 @@
"description": "A registry of transactable tokens on the network",
"author": "Parity Team <admin@parity.io>",
"version": "1.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46",
@ -64,7 +62,6 @@
"author": "Parity Team <admin@parity.io>",
"version": "1.0.0",
"visible": false,
"secure": true,
"onlyPersonal": true
},
{
@ -74,8 +71,7 @@
"description": "Have a peek at the internals of your node's transaction queue",
"author": "Parity Team <admin@parity.io>",
"version": "1.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "0x7bbc4f1a27628781b96213e781a1b8eec6982c1db8fac739af6e4c5a55862c03",
@ -84,8 +80,7 @@
"description": "Enables the registration and content management of dapps on the network",
"author": "Parity Team <admin@parity.io>",
"version": "1.0.0",
"visible": false,
"secure": true
"visible": false
},
{
"id": "0x9042323cd85c6576992d211de34b3ecc183f15e4f639aa87859882f839c374e5",

View File

@ -7,8 +7,7 @@
"description": "Display the accounts",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "account",
@ -19,7 +18,6 @@
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true,
"noselect": true
},
{
@ -30,8 +28,7 @@
"description": "Display the accounts added to the addressbook",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "address",
@ -42,7 +39,6 @@
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true,
"noselect": true
},
{
@ -53,8 +49,7 @@
"description": "Display the contracts added to the addressbook",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "contract",
@ -65,7 +60,6 @@
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true,
"noselect": true
},
{
@ -76,8 +70,7 @@
"description": "Create new contracts",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "home",
@ -87,8 +80,7 @@
"description": "Display the status of the node, recently accessed applications, accounts and news",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "playground",
@ -99,7 +91,6 @@
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true,
"noselect": true
},
{
@ -110,8 +101,7 @@
"description": "Display the current and historic Signer requests",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "settings",
@ -121,8 +111,7 @@
"description": "Allows the configuraion of all Parity & Parity-UI related options",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "status",
@ -132,8 +121,7 @@
"description": "Displays an overview of the node including settings, logs and connections",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "vaults",
@ -143,8 +131,7 @@
"description": "Allows the management of the Account Vaults, including creation, locking and account assignment",
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true
"visible": true
},
{
"id": "wallet",
@ -155,7 +142,6 @@
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true,
"noselect": true
},
{
@ -167,7 +153,6 @@
"author": "Parity Team <admin@ethcore.io>",
"version": "2.0.0",
"visible": true,
"secure": true,
"noselect": true
}
]

View File

@ -22,6 +22,7 @@ import { connect } from 'react-redux';
import { Errors } from '@parity/ui';
import Connection from '../Connection';
import DappFilter from '../DappFilter';
import Extension from '../Extension';
import FirstRun from '../FirstRun';
import ParityBar from '../ParityBar';
@ -82,6 +83,7 @@ class Application extends Component {
: null
}
<Connection />
<DappFilter />
<Requests />
<ParityBar dapp={ isMinimized } />
</div>

View File

@ -21,7 +21,7 @@ import { FormattedMessage } from 'react-intl';
import { AccountCard, Portal, SelectionList } from '@parity/ui';
@observer
export default class DappPermissions extends Component {
export default class DappAccounts extends Component {
static propTypes = {
permissionStore: PropTypes.object.isRequired
};
@ -39,7 +39,7 @@ export default class DappPermissions extends Component {
open
title={
<FormattedMessage
id='dapps.permissions.label'
id='dapps.accounts.label'
defaultMessage='visible dapp accounts'
/>
}

View File

@ -17,19 +17,19 @@
import { shallow } from 'enzyme';
import React from 'react';
import DappPermissions from './';
import DappAccounts from './';
let component;
function renderShallow (permissionStore = {}) {
component = shallow(
<DappPermissions permissionStore={ permissionStore } />
<DappAccounts permissionStore={ permissionStore } />
);
return component;
}
describe('shell/DappPermissions', () => {
describe('shell/DappAccounts', () => {
describe('rendering', () => {
it('renders defaults', () => {
expect(renderShallow()).to.be.ok;

View File

@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './dappPermissions';
export default from './dappAccounts';

View File

@ -0,0 +1,17 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './request';

View File

@ -0,0 +1,64 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button } from '@parity/ui';
export default function Request ({ className, approveRequest, denyRequest, queueId, request: { from, method } }) {
const _onApprove = () => approveRequest(queueId);
const _onReject = () => denyRequest(queueId);
return (
<div className={ className }>
<FormattedMessage
id='dappFilter.request.info'
defaultMessage='Received request from {method} from {from}'
values={ {
from,
method
} }
/>
<Button
label={
<FormattedMessage
id='dappFilter.request.buttons.approve'
defaultMessage='Approve request'
/>
}
onClick={ _onApprove }
/>
<Button
label={
<FormattedMessage
id='dappFilter.request.buttons.reject'
defaultMessage='Reject request'
/>
}
onClick={ _onReject }
/>
</div>
);
}
Request.propTypes = {
className: PropTypes.string,
approveRequest: PropTypes.func.isRequired,
denyRequest: PropTypes.func.isRequired,
queueId: PropTypes.number.isRequired,
request: PropTypes.object.isRequired
};

View File

@ -0,0 +1,34 @@
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
/* This file is part of Parity.
/*
/* Parity is free software: you can redistribute it and/or modify
/* it under the terms of the GNU General Public License as published by
/* the Free Software Foundation, either version 3 of the License, or
/* (at your option) any later version.
/*
/* Parity is distributed in the hope that it will be useful,
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/* GNU General Public License for more details.
/*
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.filter {
background: #f80;
color: white;
bottom: 0;
position: fixed;
left: 0;
right: 0;
z-index: 760; /* sits above requests */
.request {
padding: 0.5em;
> span {
margin-right: 1em;
}
}
}

View File

@ -14,52 +14,36 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default class DappFilter {
constructor (provider, permissions) {
this.permissions = permissions;
this.provider = provider;
import { observer } from 'mobx-react';
import React from 'react';
window.addEventListener('message', this.receiveMessage, false);
import Request from './Request';
import Store from './store';
import styles from './dappFilter.css';
function DappFilter () {
const store = Store.get();
if (!store || !store.hasRequests) {
return null;
}
receiveMessage = ({ data: { id, from, method, params, token }, origin, source }) => {
if (from === 'shell' || from !== token) {
return;
}
if (this.permissions.filtered.includes(method) && !this.permissions.tokens[token][method]) {
source.postMessage({
id,
from: 'shell',
error: new Error(`Method ${method} is not available to application`),
result: null,
token
}, '*');
return;
}
this.provider.send(method, params, (error, result) => {
source.postMessage({
error,
id,
from: 'shell',
result,
token
}, '*');
});
}
setPermissions (permissions) {
this.permissions = permissions;
}
static instance = null;
static create (provider, permissions) {
DappFilter.instance = new DappFilter(provider, permissions);
}
static get () {
return DappFilter.instance;
}
return (
<div className={ styles.filter }>
{
store.requests.map(({ queueId, request: { data } }) => (
<Request
className={ styles.request }
approveRequest={ store.approveRequest }
denyRequest={ store.rejectRequest }
key={ queueId }
queueId={ queueId }
request={ data }
/>
))
}
</div>
);
}
export default observer(DappFilter);

View File

@ -14,4 +14,5 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export Store from './store';
export default from './dappFilter';

View File

@ -0,0 +1,114 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { action, computed, observable } from 'mobx';
let nextQueueId = 0;
export default class Store {
@observable permissions = [];
@observable requests = [];
constructor (provider, permissions) {
this.permissions = permissions;
this.provider = provider;
window.addEventListener('message', this.receiveMessage, false);
}
@computed get hasRequests () {
return this.requests.length !== 0;
}
@action removeRequest = (_queueId) => {
this.requests = this.requests.filter(({ queueId }) => queueId !== _queueId);
}
@action queueRequest = (request) => {
let queueId = ++nextQueueId;
this.requests = this.requests.concat([{ queueId, request }]);
}
@action approveRequest = (queueId) => {
const { request: { data, source } } = this.findRequest(queueId);
this.removeRequest(queueId);
this.executeOnProvider(data, source);
}
@action rejectRequest = (queueId) => {
const { request: { data: { id, method, token }, source } } = this.findRequest(queueId);
this.removeRequest(queueId);
source.postMessage({
error: `Method ${method} not allowed`,
id,
from: 'shell',
result: null,
token
}, '*');
}
@action setPermissions = (permissions) => {
this.permissions = permissions;
}
findRequest (_queueId) {
return this.requests.find(({ queueId }) => queueId === _queueId);
}
executeOnProvider = ({ id, from, method, params, token }, source) => {
this.provider.send(method, params, (error, result) => {
source.postMessage({
error: error
? error.message
: null,
id,
from: 'shell',
result,
token
}, '*');
});
}
receiveMessage = ({ data, origin, source }) => {
const { from, method, token } = data;
if (from === 'shell' || from !== token) {
return;
}
if (this.permissions.filtered.includes(method)) {
if (!this.permissions.tokens[token] || !this.permissions.tokens[token][method]) {
this.queueRequest({ data, origin, source });
return;
}
}
this.executeOnProvider(data, source);
}
static instance = null;
static create (provider, permissions) {
Store.instance = new Store(provider, permissions);
}
static get () {
return Store.instance;
}
}

View File

@ -24,8 +24,8 @@ import { Actionbar, Button, Checkbox, DappCard, Page, SectionList } from '@parit
import { LockedIcon, VisibleIcon } from '@parity/ui/Icons';
import DappsVisible from '../DappsVisible';
import DappPermissions from '../DappPermissions';
import PermissionStore from '../DappPermissions/store';
import DappAccounts from '../DappAccounts';
import PermissionStore from '../DappAccounts/store';
import DappsStore from './dappsStore';
@ -80,7 +80,7 @@ class Dapps extends Component {
return (
<div>
<DappPermissions permissionStore={ this.permissionStore } />
<DappAccounts permissionStore={ this.permissionStore } />
<DappsVisible store={ this.store } />
<Actionbar
className={ styles.toolbar }
@ -104,11 +104,11 @@ class Dapps extends Component {
/>,
<Button
icon={ <LockedIcon /> }
key='permissions'
key='accounts'
label={
<FormattedMessage
id='dapps.button.permissions'
defaultMessage='permissions'
id='dapps.button.accounts'
defaultMessage='visible accounts'
/>
}
onClick={ this.openPermissionsModal }

View File

@ -41,7 +41,7 @@ import SecureApi from '~/secureApi';
import Application from './Application';
import Dapp from './Dapp';
import DappFilter from './DappFilter';
import { Store as DappFilterStore } from './DappFilter';
import Dapps from './Dapps';
injectTapEventPlugin();
@ -67,10 +67,11 @@ const api = new SecureApi(uiUrl, token);
patchApi(api);
ContractInstances.get(api);
DappFilter.create(api.provider, {
filtered: [],
tokens: {
}
DappFilterStore.create(api.provider, {
filtered: [
'parity_hashContent'
],
tokens: {}
});
const store = initStore(api, hashHistory);

View File

@ -28,8 +28,5 @@
<div class="loading">Loading</div>
</div>
<script src="vendor.js"></script>
<% if (!htmlWebpackPlugin.options.secure) { %>
<script src="/parity-utils/parity.js"></script>
<% } %>
</body>
</html>