Signer provenance (#4477)
* Signer - Tracking Request Provenance * Basic UI * Changing messages * VecDeque::from * Fix dapps tests * Addressing UI grumbles
This commit is contained in:
		
							parent
							
								
									d925cc05da
								
							
						
					
					
						commit
						5369a129ae
					
				
							
								
								
									
										3
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -25,10 +25,9 @@ dependencies = [ | ||||
|  "ethcore-util 1.6.0", | ||||
|  "ethsync 1.6.0", | ||||
|  "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", | ||||
|  "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)", | ||||
|  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  | ||||
| @ -16,7 +16,6 @@ number_prefix = "0.2" | ||||
| rpassword = "0.2.1" | ||||
| semver = "0.5" | ||||
| ansi_term = "0.7" | ||||
| lazy_static = "0.2" | ||||
| regex = "0.1" | ||||
| isatty = "0.1" | ||||
| toml = "0.2" | ||||
| @ -24,7 +23,7 @@ serde = "0.9" | ||||
| serde_json = "0.9" | ||||
| app_dirs = "1.1.1" | ||||
| fdlimit = "0.1" | ||||
| hyper = { version = "0.9", default-features = false } | ||||
| hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } | ||||
| ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" } | ||||
| jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } | ||||
| ethsync = { path = "sync" } | ||||
|  | ||||
| @ -77,8 +77,7 @@ impl HttpMetaExtractor<Metadata> for MetadataExtractor { | ||||
| 					}) | ||||
| 			}); | ||||
| 		Metadata { | ||||
| 			dapp_id: dapp_id, | ||||
| 			origin: Origin::Dapps, | ||||
| 			origin: Origin::Dapps(dapp_id.map(Into::into).unwrap_or_default()), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -55,8 +55,8 @@ fn should_extract_metadata() { | ||||
| 	// given
 | ||||
| 	let mut io = MetaIoHandler::default(); | ||||
| 	io.add_method_with_meta("rpc_test", |_params, meta: Metadata| { | ||||
| 		assert_eq!(meta.dapp_id, Some("https://parity.io/".to_owned())); | ||||
| 		assert_eq!(meta.origin, Origin::Dapps); | ||||
| 		assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into())); | ||||
| 		assert_eq!(meta.dapp_id(), "https://parity.io/".into()); | ||||
| 		future::ok(Value::String("Hello World!".into())).boxed() | ||||
| 	}); | ||||
| 	let server = serve_with_rpc(io); | ||||
| @ -89,8 +89,8 @@ fn should_extract_metadata_from_custom_header() { | ||||
| 	// given
 | ||||
| 	let mut io = MetaIoHandler::default(); | ||||
| 	io.add_method_with_meta("rpc_test", |_params, meta: Metadata| { | ||||
| 		assert_eq!(meta.dapp_id, Some("https://parity.io/".to_owned())); | ||||
| 		assert_eq!(meta.origin, Origin::Dapps); | ||||
| 		assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into())); | ||||
| 		assert_eq!(meta.dapp_id(), "https://parity.io/".into()); | ||||
| 		future::ok(Value::String("Hello World!".into())).boxed() | ||||
| 	}); | ||||
| 	let server = serve_with_rpc(io); | ||||
|  | ||||
| @ -649,8 +649,8 @@ mod tests { | ||||
| 	use account_provider::AccountProvider; | ||||
| 	use spec::Spec; | ||||
| 	use engines::{Engine, EngineError, Seal}; | ||||
| 	use super::*; | ||||
| 	use super::message::*; | ||||
| 	use super::{Step, View, Height, message_info_rlp, message_full_rlp}; | ||||
| 	use super::message::VoteStep; | ||||
| 
 | ||||
| 	/// Accounts inserted with "0" and "1" are validators. First proposer is "0".
 | ||||
| 	fn setup() -> (Spec, Arc<AccountProvider>) { | ||||
|  | ||||
| @ -305,7 +305,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras { | ||||
| 	} | ||||
| 
 | ||||
| 	fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> { | ||||
| 		let trace_position_deq = trace_position.into_iter().collect::<VecDeque<usize>>(); | ||||
| 		let trace_position_deq = VecDeque::from(trace_position); | ||||
| 		self.extras.block_hash(block_number) | ||||
| 			.and_then(|block_hash| self.transactions_traces(&block_hash) | ||||
| 				.and_then(|traces| traces.into_iter().nth(tx_position)) | ||||
|  | ||||
| @ -203,6 +203,13 @@ export function outSignerRequest (request) { | ||||
|           request[key].signTransaction = outTransaction(request[key].signTransaction); | ||||
|           request[key].sendTransaction = outTransaction(request[key].sendTransaction); | ||||
|           break; | ||||
| 
 | ||||
|         case 'origin': | ||||
|           const type = Object.keys(request[key])[0]; | ||||
|           const details = request[key][type]; | ||||
| 
 | ||||
|           request[key] = { type, details }; | ||||
|           break; | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
| @ -28,6 +28,7 @@ export default class Ws extends JsonRpcBase { | ||||
|     this._url = url; | ||||
|     this._token = token; | ||||
|     this._messages = {}; | ||||
|     this._sessionHash = null; | ||||
| 
 | ||||
|     this._connecting = false; | ||||
|     this._connected = false; | ||||
| @ -78,12 +79,14 @@ export default class Ws extends JsonRpcBase { | ||||
|       this._ws.onmessage = null; | ||||
|       this._ws.close(); | ||||
|       this._ws = null; | ||||
|       this._sessionHash = null; | ||||
|     } | ||||
| 
 | ||||
|     this._connecting = true; | ||||
|     this._connected = false; | ||||
|     this._lastError = null; | ||||
| 
 | ||||
|     this._sessionHash = sha3; | ||||
|     this._ws = new WebSocket(this._url, hash); | ||||
|     this._ws.onerror = this._onError; | ||||
|     this._ws.onopen = this._onOpen; | ||||
| @ -255,6 +258,10 @@ export default class Ws extends JsonRpcBase { | ||||
|     return this._token; | ||||
|   } | ||||
| 
 | ||||
|   get sessionHash () { | ||||
|     return this._sessionHash; | ||||
|   } | ||||
| 
 | ||||
|   get isAutoConnect () { | ||||
|     return this._autoConnect; | ||||
|   } | ||||
|  | ||||
							
								
								
									
										17
									
								
								js/src/views/Signer/components/RequestOrigin/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								js/src/views/Signer/components/RequestOrigin/index.js
									
									
									
									
									
										Normal 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 './requestOrigin'; | ||||
| @ -0,0 +1,43 @@ | ||||
| /* 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/>. | ||||
| */ | ||||
| 
 | ||||
| .container { | ||||
|   text-align: left; | ||||
|   margin: 3em .5em; | ||||
|   opacity: 0.6; | ||||
|   font-size: 0.8em; | ||||
| 
 | ||||
|   .unknown { | ||||
|     color: #e44; | ||||
|   } | ||||
| 
 | ||||
|   .url { | ||||
|     max-width: 100%; | ||||
|     overflow: hidden; | ||||
|     white-space: nowrap; | ||||
|     text-overflow: ellipsis; | ||||
|   } | ||||
| 
 | ||||
|   .hash { | ||||
|     margin-left: .2em; | ||||
|   } | ||||
| 
 | ||||
|   .hash, .url { | ||||
|     margin-bottom: -.2em; | ||||
|     display: inline-block; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										116
									
								
								js/src/views/Signer/components/RequestOrigin/requestOrigin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								js/src/views/Signer/components/RequestOrigin/requestOrigin.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | ||||
| // 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, { Component, PropTypes } from 'react'; | ||||
| 
 | ||||
| import IdentityIcon from '~/ui/IdentityIcon'; | ||||
| 
 | ||||
| import styles from './requestOrigin.css'; | ||||
| 
 | ||||
| export default class RequestOrigin extends Component { | ||||
|   static contextTypes = { | ||||
|     api: PropTypes.object.isRequired | ||||
|   }; | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     origin: PropTypes.shape({ | ||||
|       type: PropTypes.oneOf(['unknown', 'dapp', 'rpc', 'ipc', 'signer']), | ||||
|       details: PropTypes.string.isRequired | ||||
|     }).isRequired | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|     const { origin } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className={ styles.container }> | ||||
|         Requested { this.renderOrigin(origin) } | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderOrigin (origin) { | ||||
|     if (origin.type === 'unknown') { | ||||
|       return ( | ||||
|         <span className={ styles.unknown }>via unknown interface</span> | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (origin.type === 'dapp') { | ||||
|       return ( | ||||
|         <span> | ||||
|           by a dapp at <span className={ styles.url }> | ||||
|             { origin.details || 'unknown URL' } | ||||
|           </span> | ||||
|         </span> | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (origin.type === 'rpc') { | ||||
|       return ( | ||||
|         <span> | ||||
|           via RPC <span className={ styles.url }> | ||||
|             ({ origin.details || 'unidentified' }) | ||||
|           </span> | ||||
|         </span> | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (origin.type === 'ipc') { | ||||
|       return ( | ||||
|         <span> | ||||
|           via IPC session | ||||
|           <span | ||||
|             className={ styles.hash } | ||||
|             title={ origin.details } | ||||
|           > | ||||
|             <IdentityIcon | ||||
|               address={ origin.details } | ||||
|               tiny | ||||
|             /> | ||||
|           </span> | ||||
|         </span> | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     if (origin.type === 'signer') { | ||||
|       return this.renderSigner(origin.details); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   renderSigner (session) { | ||||
|     if (session.substr(2) === this.context.api.transport.sessionHash) { | ||||
|       return ( | ||||
|         <span title={ session }>via current tab</span> | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <span> | ||||
|         via UI session | ||||
|         <span | ||||
|           className={ styles.hash } | ||||
|           title={ `UI Session id: ${session}` } | ||||
|         > | ||||
|           <IdentityIcon | ||||
|             address={ session } | ||||
|             tiny | ||||
|           /> | ||||
|         </span> | ||||
|       </span> | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,72 @@ | ||||
| // 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 { shallow } from 'enzyme'; | ||||
| import React from 'react'; | ||||
| 
 | ||||
| import RequestOrigin from './'; | ||||
| 
 | ||||
| const context = { | ||||
|   context: { | ||||
|     api: { | ||||
|       transport: { | ||||
|         sessionHash: '1234' | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| describe('views/Signer/components/RequestOrigin', () => { | ||||
|   it('renders unknown', () => { | ||||
|     expect(shallow( | ||||
|       <RequestOrigin origin={ { type: 'unknown', details: '' } } />, | ||||
|       context | ||||
|     ).text()).to.equal('Requested via unknown interface'); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders dapps', () => { | ||||
|     expect(shallow( | ||||
|       <RequestOrigin origin={ { type: 'dapp', details: 'http://parity.io' } } />, | ||||
|       context | ||||
|     ).text()).to.equal('Requested by a dapp at http://parity.io'); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders rpc', () => { | ||||
|     expect(shallow( | ||||
|       <RequestOrigin origin={ { type: 'rpc', details: '' } } />, | ||||
|       context | ||||
|     ).text()).to.equal('Requested via RPC (unidentified)'); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders ipc', () => { | ||||
|     expect(shallow( | ||||
|       <RequestOrigin origin={ { type: 'ipc', details: '0x1234' } } />, | ||||
|       context | ||||
|     ).text()).to.equal('Requested via IPC session<Connect(IdentityIcon) />'); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders signer', () => { | ||||
|     expect(shallow( | ||||
|       <RequestOrigin origin={ { type: 'signer', details: '0x12345' } } />, | ||||
|       context | ||||
|     ).text()).to.equal('Requested via UI session<Connect(IdentityIcon) />'); | ||||
| 
 | ||||
|     expect(shallow( | ||||
|       <RequestOrigin origin={ { type: 'signer', details: '0x1234' } } />, | ||||
|       context | ||||
|     ).text()).to.equal('Requested via current tab'); | ||||
|   }); | ||||
| }); | ||||
| @ -30,6 +30,7 @@ export default class RequestPending extends Component { | ||||
|     isTest: PropTypes.bool.isRequired, | ||||
|     onConfirm: PropTypes.func.isRequired, | ||||
|     onReject: PropTypes.func.isRequired, | ||||
|     origin: PropTypes.object.isRequired, | ||||
|     payload: PropTypes.oneOfType([ | ||||
|       PropTypes.shape({ sendTransaction: PropTypes.object.isRequired }), | ||||
|       PropTypes.shape({ sign: PropTypes.object.isRequired }), | ||||
| @ -51,7 +52,7 @@ export default class RequestPending extends Component { | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|     const { className, date, focus, gasLimit, id, isSending, isTest, onReject, payload, store } = this.props; | ||||
|     const { className, date, focus, gasLimit, id, isSending, isTest, onReject, payload, store, origin } = this.props; | ||||
| 
 | ||||
|     if (payload.sign) { | ||||
|       const { sign } = payload; | ||||
| @ -68,6 +69,7 @@ export default class RequestPending extends Component { | ||||
|           isTest={ isTest } | ||||
|           onConfirm={ this.onConfirm } | ||||
|           onReject={ onReject } | ||||
|           origin={ origin } | ||||
|           store={ store } | ||||
|         /> | ||||
|       ); | ||||
| @ -87,6 +89,7 @@ export default class RequestPending extends Component { | ||||
|           isTest={ isTest } | ||||
|           onConfirm={ this.onConfirm } | ||||
|           onReject={ onReject } | ||||
|           origin={ origin } | ||||
|           store={ store } | ||||
|           transaction={ transaction } | ||||
|         /> | ||||
|  | ||||
| @ -19,6 +19,7 @@ import { observer } from 'mobx-react'; | ||||
| 
 | ||||
| import Account from '../Account'; | ||||
| import TransactionPendingForm from '../TransactionPendingForm'; | ||||
| import RequestOrigin from '../RequestOrigin'; | ||||
| 
 | ||||
| import styles from './signRequest.css'; | ||||
| 
 | ||||
| @ -40,9 +41,9 @@ export default class SignRequest extends Component { | ||||
|   }; | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     id: PropTypes.object.isRequired, | ||||
|     address: PropTypes.string.isRequired, | ||||
|     data: PropTypes.string.isRequired, | ||||
|     id: PropTypes.object.isRequired, | ||||
|     isFinished: PropTypes.bool.isRequired, | ||||
|     isTest: PropTypes.bool.isRequired, | ||||
|     store: PropTypes.object.isRequired, | ||||
| @ -52,11 +53,16 @@ export default class SignRequest extends Component { | ||||
|     isSending: PropTypes.bool, | ||||
|     onConfirm: PropTypes.func, | ||||
|     onReject: PropTypes.func, | ||||
|     origin: PropTypes.any, | ||||
|     status: PropTypes.string | ||||
|   }; | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     focus: false | ||||
|     focus: false, | ||||
|     origin: { | ||||
|       type: 'unknown', | ||||
|       details: '' | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   componentWillMount () { | ||||
| @ -92,7 +98,7 @@ export default class SignRequest extends Component { | ||||
| 
 | ||||
|   renderDetails () { | ||||
|     const { api } = this.context; | ||||
|     const { address, isTest, store, data } = this.props; | ||||
|     const { address, isTest, store, data, origin } = this.props; | ||||
|     const { balances, externalLink } = store; | ||||
| 
 | ||||
|     const balance = balances[address]; | ||||
| @ -110,6 +116,7 @@ export default class SignRequest extends Component { | ||||
|             externalLink={ externalLink } | ||||
|             isTest={ isTest } | ||||
|           /> | ||||
|           <RequestOrigin origin={ origin } /> | ||||
|         </div> | ||||
|         <div className={ styles.info } title={ api.util.sha3(data) }> | ||||
|           <p>A request to sign data using your account:</p> | ||||
|  | ||||
| @ -39,6 +39,7 @@ | ||||
|   width: 40%; | ||||
|   vertical-align: top; | ||||
| 
 | ||||
|   .account { | ||||
|     img { | ||||
|       display: inline-block; | ||||
|       width: 50px; | ||||
| @ -49,6 +50,7 @@ | ||||
|     span { | ||||
|       display: block; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .method { | ||||
|  | ||||
| @ -22,6 +22,8 @@ import { Button, MethodDecoding } from '~/ui'; | ||||
| 
 | ||||
| import * as tUtil from '../util/transaction'; | ||||
| import Account from '../Account'; | ||||
| import RequestOrigin from '../RequestOrigin'; | ||||
| 
 | ||||
| import styles from './transactionMainDetails.css'; | ||||
| 
 | ||||
| export default class TransactionMainDetails extends Component { | ||||
| @ -33,11 +35,19 @@ export default class TransactionMainDetails extends Component { | ||||
|     gasStore: PropTypes.object, | ||||
|     id: PropTypes.object.isRequired, | ||||
|     isTest: PropTypes.bool.isRequired, | ||||
|     origin: PropTypes.any, | ||||
|     totalValue: PropTypes.object.isRequired, | ||||
|     transaction: PropTypes.object.isRequired, | ||||
|     value: PropTypes.object.isRequired | ||||
|   }; | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     origin: { | ||||
|       type: 'unknown', | ||||
|       details: '' | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   componentWillMount () { | ||||
|     const { totalValue, value } = this.props; | ||||
| 
 | ||||
| @ -51,7 +61,7 @@ export default class TransactionMainDetails extends Component { | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { children, externalLink, from, fromBalance, gasStore, isTest, transaction } = this.props; | ||||
|     const { children, externalLink, from, fromBalance, gasStore, isTest, transaction, origin } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className={ styles.transaction }> | ||||
| @ -64,6 +74,7 @@ export default class TransactionMainDetails extends Component { | ||||
|               isTest={ isTest } | ||||
|             /> | ||||
|           </div> | ||||
|           <RequestOrigin origin={ origin } /> | ||||
|         </div> | ||||
|         <div className={ styles.method }> | ||||
|           <MethodDecoding | ||||
|  | ||||
| @ -43,6 +43,7 @@ export default class TransactionPending extends Component { | ||||
|     nonce: PropTypes.number, | ||||
|     onConfirm: PropTypes.func.isRequired, | ||||
|     onReject: PropTypes.func.isRequired, | ||||
|     origin: PropTypes.any, | ||||
|     store: PropTypes.object.isRequired, | ||||
|     transaction: PropTypes.shape({ | ||||
|       condition: PropTypes.object, | ||||
| @ -56,7 +57,11 @@ export default class TransactionPending extends Component { | ||||
|   }; | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     focus: false | ||||
|     focus: false, | ||||
|     origin: { | ||||
|       type: 'unknown', | ||||
|       details: '' | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   gasStore = new GasPriceEditor.Store(this.context.api, { | ||||
| @ -87,7 +92,7 @@ export default class TransactionPending extends Component { | ||||
|   } | ||||
| 
 | ||||
|   renderTransaction () { | ||||
|     const { className, focus, id, isSending, isTest, store, transaction } = this.props; | ||||
|     const { className, focus, id, isSending, isTest, store, transaction, origin } = this.props; | ||||
|     const { totalValue } = this.state; | ||||
|     const { balances, externalLink } = store; | ||||
|     const { from, value } = transaction; | ||||
| @ -104,6 +109,7 @@ export default class TransactionPending extends Component { | ||||
|           gasStore={ this.gasStore } | ||||
|           id={ id } | ||||
|           isTest={ isTest } | ||||
|           origin={ origin } | ||||
|           totalValue={ totalValue } | ||||
|           transaction={ transaction } | ||||
|           value={ value } | ||||
|  | ||||
| @ -81,7 +81,7 @@ class Embedded extends Component { | ||||
| 
 | ||||
|   renderPending = (data, index) => { | ||||
|     const { actions, gasLimit, isTest } = this.props; | ||||
|     const { date, id, isSending, payload } = data; | ||||
|     const { date, id, isSending, payload, origin } = data; | ||||
| 
 | ||||
|     return ( | ||||
|       <RequestPending | ||||
| @ -95,6 +95,7 @@ class Embedded extends Component { | ||||
|         key={ id } | ||||
|         onConfirm={ actions.startConfirmRequest } | ||||
|         onReject={ actions.startRejectRequest } | ||||
|         origin={ origin } | ||||
|         payload={ payload } | ||||
|         store={ this.store } | ||||
|       /> | ||||
|  | ||||
| @ -107,7 +107,7 @@ class RequestsPage extends Component { | ||||
| 
 | ||||
|   renderPending = (data, index) => { | ||||
|     const { actions, gasLimit, isTest } = this.props; | ||||
|     const { date, id, isSending, payload } = data; | ||||
|     const { date, id, isSending, payload, origin } = data; | ||||
| 
 | ||||
|     return ( | ||||
|       <RequestPending | ||||
| @ -121,6 +121,7 @@ class RequestsPage extends Component { | ||||
|         key={ id } | ||||
|         onConfirm={ actions.startConfirmRequest } | ||||
|         onReject={ actions.startRejectRequest } | ||||
|         origin={ origin } | ||||
|         payload={ payload } | ||||
|         store={ this.store } | ||||
|       /> | ||||
|  | ||||
| @ -28,7 +28,7 @@ extern crate ctrlc; | ||||
| extern crate docopt; | ||||
| extern crate env_logger; | ||||
| extern crate fdlimit; | ||||
| extern crate hyper; // for price_info.rs
 | ||||
| extern crate hyper; | ||||
| extern crate isatty; | ||||
| extern crate jsonrpc_core; | ||||
| extern crate num_cpus; | ||||
| @ -60,15 +60,14 @@ extern crate parity_reactor; | ||||
| extern crate parity_updater as updater; | ||||
| extern crate rpc_cli; | ||||
| 
 | ||||
| #[macro_use] | ||||
| extern crate log as rlog; | ||||
| 
 | ||||
| #[cfg(feature="stratum")] | ||||
| extern crate ethcore_stratum; | ||||
| #[cfg(feature = "dapps")] | ||||
| extern crate ethcore_dapps; | ||||
| 
 | ||||
| 
 | ||||
| #[macro_use] | ||||
| extern crate log as rlog; | ||||
| 
 | ||||
| macro_rules! dependency { | ||||
| 	($dep_ty:ident, $url:expr) => { | ||||
| 		{ | ||||
|  | ||||
| @ -21,9 +21,10 @@ use std::io; | ||||
| use io::PanicHandler; | ||||
| 
 | ||||
| use dir::default_data_path; | ||||
| use ethcore_rpc::{self as rpc, RpcServerError, IpcServerError, Metadata}; | ||||
| use ethcore_rpc::{self as rpc, RpcServerError, IpcServerError, Metadata, Origin}; | ||||
| use ethcore_rpc::informant::{RpcStats, Middleware}; | ||||
| use helpers::parity_ipc_path; | ||||
| use hyper; | ||||
| use jsonrpc_core::MetaIoHandler; | ||||
| use jsonrpc_core::reactor::{RpcHandler, Remote}; | ||||
| use rpc_apis; | ||||
| @ -89,6 +90,18 @@ pub struct Dependencies { | ||||
| 	pub stats: Arc<RpcStats>, | ||||
| } | ||||
| 
 | ||||
| pub struct RpcExtractor; | ||||
| impl rpc::HttpMetaExtractor<Metadata> for RpcExtractor { | ||||
| 	fn read_metadata(&self, req: &hyper::server::Request<hyper::net::HttpStream>) -> Metadata { | ||||
| 		let origin = req.headers().get::<hyper::header::Origin>() | ||||
| 			.map(|origin| format!("{}://{}", origin.scheme, origin.host)) | ||||
| 			.unwrap_or_else(|| "unknown".into()); | ||||
| 		let mut metadata = Metadata::default(); | ||||
| 		metadata.origin = Origin::Rpc(origin); | ||||
| 		metadata | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Result<Option<HttpServer>, String> { | ||||
| 	if !conf.enabled { | ||||
| 		return Ok(None); | ||||
| @ -113,7 +126,7 @@ pub fn setup_http_rpc_server( | ||||
| 	let apis = setup_apis(apis, dependencies); | ||||
| 	let handler = RpcHandler::new(Arc::new(apis), dependencies.remote.clone()); | ||||
| 	let ph = dependencies.panic_handler.clone(); | ||||
| 	let start_result = rpc::start_http(url, cors_domains, allowed_hosts, ph, handler); | ||||
| 	let start_result = rpc::start_http(url, cors_domains, allowed_hosts, ph, handler, RpcExtractor); | ||||
| 	match start_result { | ||||
| 		Err(RpcServerError::IoError(err)) => match err.kind() { | ||||
| 			io::ErrorKind::AddrInUse => Err(format!("RPC address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url)), | ||||
|  | ||||
| @ -23,12 +23,14 @@ pub use ethcore_signer::Server as SignerServer; | ||||
| use ansi_term::Colour; | ||||
| use dir::default_data_path; | ||||
| use ethcore_rpc::informant::RpcStats; | ||||
| use ethcore_rpc; | ||||
| use ethcore_signer as signer; | ||||
| use helpers::replace_home; | ||||
| use io::{ForwardPanic, PanicHandler}; | ||||
| use jsonrpc_core::reactor::{RpcHandler, Remote}; | ||||
| use rpc_apis; | ||||
| use util::path::restrict_permissions_owner; | ||||
| use util::H256; | ||||
| 
 | ||||
| const CODES_FILENAME: &'static str = "authcodes"; | ||||
| 
 | ||||
| @ -67,6 +69,16 @@ pub struct NewToken { | ||||
| 	pub message: String, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Default, Clone)] | ||||
| pub struct StandardExtractor; | ||||
| impl signer::MetaExtractor<ethcore_rpc::Metadata> for StandardExtractor { | ||||
| 	fn extract_metadata(&self, session: &H256) -> ethcore_rpc::Metadata { | ||||
| 		let mut metadata = ethcore_rpc::Metadata::default(); | ||||
| 		metadata.origin = ethcore_rpc::Origin::Signer((*session).into()); | ||||
| 		metadata | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn start(conf: Configuration, deps: Dependencies) -> Result<Option<SignerServer>, String> { | ||||
| 	if !conf.enabled { | ||||
| 		Ok(None) | ||||
| @ -133,7 +145,7 @@ fn do_start(conf: Configuration, deps: Dependencies) -> Result<SignerServer, Str | ||||
| 		let server = server.stats(deps.rpc_stats.clone()); | ||||
| 		let apis = rpc_apis::setup_rpc(deps.rpc_stats, deps.apis, rpc_apis::ApiSet::SafeContext); | ||||
| 		let handler = RpcHandler::new(Arc::new(apis), deps.remote); | ||||
| 		server.start(addr, handler) | ||||
| 		server.start_with_extractor(addr, handler, StandardExtractor) | ||||
| 	}; | ||||
| 
 | ||||
| 	match start_result { | ||||
|  | ||||
| @ -65,19 +65,24 @@ use io::PanicHandler; | ||||
| use jsonrpc_core::reactor::RpcHandler; | ||||
| 
 | ||||
| pub use ipc::{Server as IpcServer, Error as IpcServerError}; | ||||
| pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError}; | ||||
| pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError, HttpMetaExtractor}; | ||||
| pub mod v1; | ||||
| pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, Metadata, Origin, informant, dispatch}; | ||||
| pub use v1::block_import::is_major_importing; | ||||
| 
 | ||||
| /// Start http server asynchronously and returns result with `Server` handle on success or an error.
 | ||||
| pub fn start_http<M: jsonrpc_core::Metadata, S: jsonrpc_core::Middleware<M>>( | ||||
| pub fn start_http<M, T, S>( | ||||
| 	addr: &SocketAddr, | ||||
| 	cors_domains: Option<Vec<String>>, | ||||
| 	allowed_hosts: Option<Vec<String>>, | ||||
| 	panic_handler: Arc<PanicHandler>, | ||||
| 	handler: RpcHandler<M, S>, | ||||
| ) -> Result<Server, RpcServerError> { | ||||
| 	extractor: T, | ||||
| ) -> Result<Server, RpcServerError> where | ||||
| 	M: jsonrpc_core::Metadata, | ||||
| 	S: jsonrpc_core::Middleware<M>, | ||||
| 	T: HttpMetaExtractor<M>, | ||||
| { | ||||
| 
 | ||||
| 	let cors_domains = cors_domains.map(|domains| { | ||||
| 		domains.into_iter() | ||||
| @ -90,6 +95,7 @@ pub fn start_http<M: jsonrpc_core::Metadata, S: jsonrpc_core::Middleware<M>>( | ||||
| 	}); | ||||
| 
 | ||||
| 	ServerBuilder::with_rpc_handler(handler) | ||||
| 		.meta_extractor(Arc::new(extractor)) | ||||
| 		.cors(cors_domains.into()) | ||||
| 		.allowed_hosts(allowed_hosts.into()) | ||||
| 		.panic_handler(move || { | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| use util::{Address, U256, Bytes}; | ||||
| use v1::types::TransactionCondition; | ||||
| use v1::types::{Origin, TransactionCondition}; | ||||
| 
 | ||||
| /// Transaction request coming from RPC
 | ||||
| #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] | ||||
| @ -102,6 +102,8 @@ pub struct ConfirmationRequest { | ||||
| 	pub id: U256, | ||||
| 	/// Payload to confirm
 | ||||
| 	pub payload: ConfirmationPayload, | ||||
| 	/// Request origin
 | ||||
| 	pub origin: Origin, | ||||
| } | ||||
| 
 | ||||
| /// Payload to confirm in Trusted Signer
 | ||||
|  | ||||
| @ -22,8 +22,7 @@ use jsonrpc_core; | ||||
| use util::{Mutex, RwLock, U256, Address}; | ||||
| use ethcore::account_provider::DappId; | ||||
| use v1::helpers::{ConfirmationRequest, ConfirmationPayload}; | ||||
| use v1::metadata::Metadata; | ||||
| use v1::types::{ConfirmationResponse, H160 as RpcH160}; | ||||
| use v1::types::{ConfirmationResponse, H160 as RpcH160, Origin, DappId as RpcDappId}; | ||||
| 
 | ||||
| /// Result that can be returned from JSON RPC.
 | ||||
| pub type RpcResult = Result<ConfirmationResponse, jsonrpc_core::Error>; | ||||
| @ -37,9 +36,9 @@ pub enum DefaultAccount { | ||||
| 	ForDapp(DappId), | ||||
| } | ||||
| 
 | ||||
| impl From<Metadata> for DefaultAccount { | ||||
| 	fn from(meta: Metadata) -> Self { | ||||
| 		DefaultAccount::ForDapp(meta.dapp_id.unwrap_or_default().into()) | ||||
| impl From<RpcDappId> for DefaultAccount { | ||||
| 	fn from(dapp_id: RpcDappId) -> Self { | ||||
| 		DefaultAccount::ForDapp(dapp_id.into()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -84,7 +83,7 @@ const QUEUE_LIMIT: usize = 50; | ||||
| pub trait SigningQueue: Send + Sync { | ||||
| 	/// Add new request to the queue.
 | ||||
| 	/// Returns a `ConfirmationPromise` that can be used to await for resolution of given request.
 | ||||
| 	fn add_request(&self, request: ConfirmationPayload) -> Result<ConfirmationPromise, QueueAddError>; | ||||
| 	fn add_request(&self, request: ConfirmationPayload, origin: Origin) -> Result<ConfirmationPromise, QueueAddError>; | ||||
| 
 | ||||
| 	/// Removes a request from the queue.
 | ||||
| 	/// Notifies possible token holders that request was rejected.
 | ||||
| @ -267,7 +266,7 @@ impl Drop for ConfirmationsQueue { | ||||
| } | ||||
| 
 | ||||
| impl SigningQueue for ConfirmationsQueue { | ||||
| 	fn add_request(&self, request: ConfirmationPayload) -> Result<ConfirmationPromise, QueueAddError> { | ||||
| 	fn add_request(&self, request: ConfirmationPayload, origin: Origin) -> Result<ConfirmationPromise, QueueAddError> { | ||||
| 		if self.len() > QUEUE_LIMIT { | ||||
| 			return Err(QueueAddError::LimitReached); | ||||
| 		} | ||||
| @ -290,6 +289,7 @@ impl SigningQueue for ConfirmationsQueue { | ||||
| 				request: ConfirmationRequest { | ||||
| 					id: id, | ||||
| 					payload: request, | ||||
| 					origin: origin, | ||||
| 				}, | ||||
| 			}); | ||||
| 			queue.get(&id).map(|token| token.as_promise()).expect("Token was just inserted.") | ||||
| @ -362,7 +362,7 @@ mod test { | ||||
| 		// when
 | ||||
| 		let q = queue.clone(); | ||||
| 		let handle = thread::spawn(move || { | ||||
| 			let v = q.add_request(request).unwrap(); | ||||
| 			let v = q.add_request(request, Default::default()).unwrap(); | ||||
| 			let (tx, rx) = mpsc::channel(); | ||||
| 			v.wait_for_result(move |res| { | ||||
| 				tx.send(res).unwrap(); | ||||
| @ -397,7 +397,7 @@ mod test { | ||||
| 				*v = Some(notification); | ||||
| 			}).expect("Should be closed nicely.") | ||||
| 		}); | ||||
| 		queue.add_request(request).unwrap(); | ||||
| 		queue.add_request(request, Default::default()).unwrap(); | ||||
| 		queue.finish(); | ||||
| 
 | ||||
| 		// then
 | ||||
| @ -413,7 +413,7 @@ mod test { | ||||
| 		let request = request(); | ||||
| 
 | ||||
| 		// when
 | ||||
| 		queue.add_request(request.clone()).unwrap(); | ||||
| 		queue.add_request(request.clone(), Default::default()).unwrap(); | ||||
| 		let all = queue.requests(); | ||||
| 
 | ||||
| 		// then
 | ||||
|  | ||||
| @ -311,7 +311,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where | ||||
| 	} | ||||
| 
 | ||||
| 	fn author(&self, meta: Metadata) -> BoxFuture<RpcH160, Error> { | ||||
| 		let dapp = meta.dapp_id.unwrap_or_default(); | ||||
| 		let dapp = meta.dapp_id(); | ||||
| 
 | ||||
| 		let author = move || { | ||||
| 			let mut miner = take_weak!(self.miner).author(); | ||||
| @ -342,7 +342,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where | ||||
| 	} | ||||
| 
 | ||||
| 	fn accounts(&self, meta: Metadata) -> BoxFuture<Vec<RpcH160>, Error> { | ||||
| 		let dapp = meta.dapp_id.unwrap_or_default(); | ||||
| 		let dapp = meta.dapp_id(); | ||||
| 
 | ||||
| 		let accounts = move || { | ||||
| 			let accounts = self.dapp_accounts(dapp.into())?; | ||||
|  | ||||
| @ -178,7 +178,7 @@ impl Eth for EthClient { | ||||
| 	} | ||||
| 
 | ||||
| 	fn accounts(&self, meta: Metadata) -> BoxFuture<Vec<RpcH160>, Error> { | ||||
| 		let dapp: DappId = meta.dapp_id.unwrap_or_default().into(); | ||||
| 		let dapp: DappId = meta.dapp_id().into(); | ||||
| 
 | ||||
| 		let accounts = self.accounts | ||||
| 			.note_dapp_used(dapp.clone()) | ||||
|  | ||||
| @ -145,7 +145,7 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where | ||||
| 	} | ||||
| 
 | ||||
| 	fn default_account(&self, meta: Self::Metadata) -> BoxFuture<H160, Error> { | ||||
| 		let dapp_id = meta.dapp_id.unwrap_or_default(); | ||||
| 		let dapp_id = meta.dapp_id(); | ||||
| 		let default_account = move || { | ||||
| 			Ok(take_weak!(self.accounts) | ||||
| 				.dapps_addresses(dapp_id.into()) | ||||
|  | ||||
| @ -101,7 +101,7 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> { | ||||
| 		let default = match request.from.as_ref() { | ||||
| 			Some(account) => Ok(account.clone().into()), | ||||
| 			None => accounts | ||||
| 				.default_address(meta.dapp_id.unwrap_or_default().into()) | ||||
| 				.default_address(meta.dapp_id().into()) | ||||
| 				.map_err(|e| errors::account("Cannot find default account.", e)), | ||||
| 		}; | ||||
| 
 | ||||
|  | ||||
| @ -38,7 +38,8 @@ use v1::types::{ | ||||
| 	RichRawTransaction as RpcRichRawTransaction, | ||||
| 	TransactionRequest as RpcTransactionRequest, | ||||
| 	ConfirmationPayload as RpcConfirmationPayload, | ||||
| 	ConfirmationResponse as RpcConfirmationResponse | ||||
| 	ConfirmationResponse as RpcConfirmationResponse, | ||||
| 	Origin, | ||||
| }; | ||||
| 
 | ||||
| const MAX_PENDING_DURATION: u64 = 60 * 60; | ||||
| @ -81,7 +82,7 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount) -> BoxFuture<DispatchResult, Error> { | ||||
| 	fn dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount, origin: Origin) -> BoxFuture<DispatchResult, Error> { | ||||
| 		let accounts = take_weakf!(self.accounts); | ||||
| 		let default_account = match default_account { | ||||
| 			DefaultAccount::Provided(acc) => acc, | ||||
| @ -100,7 +101,7 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> { | ||||
| 						.boxed() | ||||
| 				} else { | ||||
| 					future::done( | ||||
| 						signer.add_request(payload) | ||||
| 						signer.add_request(payload, origin) | ||||
| 							.map(DispatchResult::Promise) | ||||
| 							.map_err(|_| errors::request_rejected_limit()) | ||||
| 					).boxed() | ||||
| @ -113,10 +114,13 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> { | ||||
| impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> { | ||||
| 	type Metadata = Metadata; | ||||
| 
 | ||||
| 	fn post_sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> { | ||||
| 	fn post_sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> { | ||||
| 		let pending = self.pending.clone(); | ||||
| 		self.dispatch(RpcConfirmationPayload::Signature((address.clone(), data).into()), DefaultAccount::Provided(address.into())) | ||||
| 			.map(move |result| match result { | ||||
| 		self.dispatch( | ||||
| 			RpcConfirmationPayload::Signature((address.clone(), data).into()), | ||||
| 			DefaultAccount::Provided(address.into()), | ||||
| 			meta.origin | ||||
| 		).map(move |result| match result { | ||||
| 			DispatchResult::Value(v) => RpcEither::Or(v), | ||||
| 			DispatchResult::Promise(promise) => { | ||||
| 				let id = promise.id(); | ||||
| @ -129,7 +133,7 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> { | ||||
| 
 | ||||
| 	fn post_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> { | ||||
| 		let pending = self.pending.clone(); | ||||
| 		self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.into()) | ||||
| 		self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.dapp_id().into(), meta.origin) | ||||
| 			.map(move |result| match result { | ||||
| 				DispatchResult::Value(v) => RpcEither::Or(v), | ||||
| 				DispatchResult::Promise(promise) => { | ||||
| @ -156,8 +160,12 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> { | ||||
| 		res | ||||
| 	} | ||||
| 
 | ||||
| 	fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcBytes, Error> { | ||||
| 		let res = self.dispatch(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into()); | ||||
| 	fn decrypt_message(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcBytes, Error> { | ||||
| 		let res = self.dispatch( | ||||
| 			RpcConfirmationPayload::Decrypt((address.clone(), data).into()), | ||||
| 			address.into(), | ||||
| 			meta.origin, | ||||
| 		); | ||||
| 
 | ||||
| 		let (ready, p) = futures::oneshot(); | ||||
| 
 | ||||
| @ -181,8 +189,12 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> { | ||||
| impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> { | ||||
| 	type Metadata = Metadata; | ||||
| 
 | ||||
| 	fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> { | ||||
| 		let res = self.dispatch(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into()); | ||||
| 	fn sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> { | ||||
| 		let res = self.dispatch( | ||||
| 			RpcConfirmationPayload::Signature((address.clone(), data).into()), | ||||
| 			address.into(), | ||||
| 			meta.origin, | ||||
| 		); | ||||
| 
 | ||||
| 		let (ready, p) = futures::oneshot(); | ||||
| 
 | ||||
| @ -200,7 +212,11 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> { | ||||
| 	} | ||||
| 
 | ||||
| 	fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcH256, Error> { | ||||
| 		let res = self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.into()); | ||||
| 		let res = self.dispatch( | ||||
| 			RpcConfirmationPayload::SendTransaction(request), | ||||
| 			meta.dapp_id().into(), | ||||
| 			meta.origin, | ||||
| 		); | ||||
| 
 | ||||
| 		let (ready, p) = futures::oneshot(); | ||||
| 
 | ||||
| @ -218,7 +234,11 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> { | ||||
| 	} | ||||
| 
 | ||||
| 	fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcRichRawTransaction, Error> { | ||||
| 		let res = self.dispatch(RpcConfirmationPayload::SignTransaction(request), meta.into()); | ||||
| 		let res = self.dispatch( | ||||
| 			RpcConfirmationPayload::SignTransaction(request), | ||||
| 			meta.dapp_id().into(), | ||||
| 			meta.origin, | ||||
| 		); | ||||
| 
 | ||||
| 		let (ready, p) = futures::oneshot(); | ||||
| 
 | ||||
|  | ||||
| @ -72,7 +72,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D> | ||||
| { | ||||
| 	type Metadata = Metadata; | ||||
| 
 | ||||
| 	fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> { | ||||
| 	fn sign(&self, _: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> { | ||||
| 		self.handle(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into()) | ||||
| 			.then(|res| match res { | ||||
| 				Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature), | ||||
| @ -83,7 +83,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D> | ||||
| 	} | ||||
| 
 | ||||
| 	fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcH256, Error> { | ||||
| 		self.handle(RpcConfirmationPayload::SendTransaction(request), meta.into()) | ||||
| 		self.handle(RpcConfirmationPayload::SendTransaction(request), meta.dapp_id().into()) | ||||
| 			.then(|res| match res { | ||||
| 				Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash), | ||||
| 				Err(e) => Err(e), | ||||
| @ -93,7 +93,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D> | ||||
| 	} | ||||
| 
 | ||||
| 	fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcRichRawTransaction, Error> { | ||||
| 		self.handle(RpcConfirmationPayload::SignTransaction(request), meta.into()) | ||||
| 		self.handle(RpcConfirmationPayload::SignTransaction(request), meta.dapp_id().into()) | ||||
| 			.then(|res| match res { | ||||
| 				Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx), | ||||
| 				Err(e) => Err(e), | ||||
| @ -106,7 +106,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D> | ||||
| impl<D: Dispatcher + 'static> ParitySigning for SigningUnsafeClient<D> { | ||||
| 	type Metadata = Metadata; | ||||
| 
 | ||||
| 	fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcBytes, Error> { | ||||
| 	fn decrypt_message(&self, _: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcBytes, Error> { | ||||
| 		self.handle(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into()) | ||||
| 			.then(|res| match res { | ||||
| 				Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data), | ||||
| @ -116,7 +116,7 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningUnsafeClient<D> { | ||||
| 			.boxed() | ||||
| 	} | ||||
| 
 | ||||
| 	fn post_sign(&self, _: RpcH160, _: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> { | ||||
| 	fn post_sign(&self, _: Metadata,  _: RpcH160, _: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> { | ||||
| 		// We don't support this in non-signer mode.
 | ||||
| 		future::err(errors::signer_disabled()).boxed() | ||||
| 	} | ||||
|  | ||||
| @ -16,33 +16,22 @@ | ||||
| 
 | ||||
| use jsonrpc_core; | ||||
| 
 | ||||
| use v1::types::{DappId, Origin}; | ||||
| 
 | ||||
| /// RPC methods metadata.
 | ||||
| #[derive(Clone, Default, Debug, PartialEq)] | ||||
| pub struct Metadata { | ||||
| 	/// Current dapplication identifier
 | ||||
| 	pub dapp_id: Option<String>, | ||||
| 	/// Request origin
 | ||||
| 	pub origin: Origin, | ||||
| } | ||||
| 
 | ||||
| /// RPC request origin
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| pub enum Origin { | ||||
| 	/// RPC server
 | ||||
| 	Rpc, | ||||
| 	/// Dapps server
 | ||||
| 	Dapps, | ||||
| 	/// IPC server
 | ||||
| 	Ipc, | ||||
| 	/// Signer
 | ||||
| 	Signer, | ||||
| 	/// Unknown
 | ||||
| 	Unknown, | ||||
| } | ||||
| 
 | ||||
| impl Default for Origin { | ||||
| 	fn default() -> Self { | ||||
| 		Origin::Unknown | ||||
| impl Metadata { | ||||
| 	/// Get
 | ||||
| 	pub fn dapp_id(&self) -> DappId { | ||||
| 		match self.origin { | ||||
| 			Origin::Dapps(ref dapp_id) => dapp_id.clone(), | ||||
| 			_ => DappId::default(), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -61,4 +61,5 @@ pub mod types; | ||||
| pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Net, Parity, ParityAccounts, ParitySet, ParitySigning, Signer, Personal, Traces, Rpc}; | ||||
| pub use self::impls::*; | ||||
| pub use self::helpers::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, block_import, informant, dispatch}; | ||||
| pub use self::metadata::{Metadata, Origin}; | ||||
| pub use self::metadata::Metadata; | ||||
| pub use self::types::Origin; | ||||
|  | ||||
| @ -37,6 +37,7 @@ use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSignin | ||||
| use v1::helpers::dispatch::FullDispatcher; | ||||
| use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService}; | ||||
| use v1::metadata::Metadata; | ||||
| use v1::types::Origin; | ||||
| 
 | ||||
| fn blockchain_client() -> Arc<TestBlockChainClient> { | ||||
| 	let client = TestBlockChainClient::new(); | ||||
| @ -387,7 +388,7 @@ fn rpc_eth_accounts() { | ||||
| 	let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; | ||||
| 	let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#; | ||||
| 	let mut meta = Metadata::default(); | ||||
| 	meta.dapp_id = Some("app1".into()); | ||||
| 	meta.origin = Origin::Dapps("app1".into()); | ||||
| 	assert_eq!((*tester.io).handle_request_sync(request, meta), Some(response.to_owned())); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -25,7 +25,7 @@ use rlp::encode; | ||||
| 
 | ||||
| use serde_json; | ||||
| use jsonrpc_core::IoHandler; | ||||
| use v1::{SignerClient, Signer}; | ||||
| use v1::{SignerClient, Signer, Origin}; | ||||
| use v1::metadata::Metadata; | ||||
| use v1::tests::helpers::TestMinerService; | ||||
| use v1::helpers::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload}; | ||||
| @ -88,15 +88,15 @@ fn should_return_list_of_items_to_confirm() { | ||||
| 		data: vec![], | ||||
| 		nonce: None, | ||||
| 		condition: None, | ||||
| 	})).unwrap(); | ||||
| 	tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into())).unwrap(); | ||||
| 	}), Origin::Dapps("http://parity.io".into())).unwrap(); | ||||
| 	tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into()), Origin::Unknown).unwrap(); | ||||
| 
 | ||||
| 	// when
 | ||||
| 	let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; | ||||
| 	let response = concat!( | ||||
| 		r#"{"jsonrpc":"2.0","result":["#, | ||||
| 		r#"{"id":"0x1","payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, | ||||
| 		r#"{"id":"0x2","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#, | ||||
| 		r#"{"id":"0x1","origin":{"dapp":"http://parity.io"},"payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, | ||||
| 		r#"{"id":"0x2","origin":"unknown","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#, | ||||
| 		r#"],"id":1}"# | ||||
| 	); | ||||
| 
 | ||||
| @ -119,7 +119,7 @@ fn should_reject_transaction_from_queue_without_dispatching() { | ||||
| 		data: vec![], | ||||
| 		nonce: None, | ||||
| 		condition: None, | ||||
| 	})).unwrap(); | ||||
| 	}), Origin::Unknown).unwrap(); | ||||
| 	assert_eq!(tester.signer.requests().len(), 1); | ||||
| 
 | ||||
| 	// when
 | ||||
| @ -146,7 +146,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { | ||||
| 		data: vec![], | ||||
| 		nonce: None, | ||||
| 		condition: None, | ||||
| 	})).unwrap(); | ||||
| 	}), Origin::Unknown).unwrap(); | ||||
| 	assert_eq!(tester.signer.requests().len(), 1); | ||||
| 
 | ||||
| 	// when
 | ||||
| @ -162,7 +162,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { | ||||
| fn should_not_remove_sign_if_password_is_invalid() { | ||||
| 	// given
 | ||||
| 	let tester = signer_tester(); | ||||
| 	tester.signer.add_request(ConfirmationPayload::Signature(0.into(), vec![5].into())).unwrap(); | ||||
| 	tester.signer.add_request(ConfirmationPayload::Signature(0.into(), vec![5].into()), Origin::Unknown).unwrap(); | ||||
| 	assert_eq!(tester.signer.requests().len(), 1); | ||||
| 
 | ||||
| 	// when
 | ||||
| @ -190,7 +190,7 @@ fn should_confirm_transaction_and_dispatch() { | ||||
| 		data: vec![], | ||||
| 		nonce: None, | ||||
| 		condition: None, | ||||
| 	})).unwrap(); | ||||
| 	}), Origin::Unknown).unwrap(); | ||||
| 
 | ||||
| 	let t = Transaction { | ||||
| 		nonce: U256::zero(), | ||||
| @ -236,7 +236,7 @@ fn should_alter_the_sender_and_nonce() { | ||||
| 		data: vec![], | ||||
| 		nonce: Some(10.into()), | ||||
| 		condition: None, | ||||
| 	})).unwrap(); | ||||
| 	}), Origin::Unknown).unwrap(); | ||||
| 
 | ||||
| 	let t = Transaction { | ||||
| 		nonce: U256::zero(), | ||||
| @ -286,7 +286,7 @@ fn should_confirm_transaction_with_token() { | ||||
| 		data: vec![], | ||||
| 		nonce: None, | ||||
| 		condition: None, | ||||
| 	})).unwrap(); | ||||
| 	}), Origin::Unknown).unwrap(); | ||||
| 
 | ||||
| 	let t = Transaction { | ||||
| 		nonce: U256::zero(), | ||||
| @ -335,7 +335,7 @@ fn should_confirm_transaction_with_rlp() { | ||||
| 		data: vec![], | ||||
| 		nonce: None, | ||||
| 		condition: None, | ||||
| 	})).unwrap(); | ||||
| 	}), Origin::Unknown).unwrap(); | ||||
| 
 | ||||
| 	let t = Transaction { | ||||
| 		nonce: U256::zero(), | ||||
| @ -383,7 +383,7 @@ fn should_return_error_when_sender_does_not_match() { | ||||
| 		data: vec![], | ||||
| 		nonce: None, | ||||
| 		condition: None, | ||||
| 	})).unwrap(); | ||||
| 	}), Origin::Unknown).unwrap(); | ||||
| 
 | ||||
| 	let t = Transaction { | ||||
| 		nonce: U256::zero(), | ||||
|  | ||||
| @ -27,8 +27,8 @@ build_rpc_trait! { | ||||
| 		type Metadata; | ||||
| 
 | ||||
| 		/// Signs the hash of data with given address signature.
 | ||||
| 		#[rpc(async, name = "eth_sign")] | ||||
| 		fn sign(&self, H160, Bytes) -> BoxFuture<H520, Error>; | ||||
| 		#[rpc(meta, name = "eth_sign")] | ||||
| 		fn sign(&self, Self::Metadata, H160, Bytes) -> BoxFuture<H520, Error>; | ||||
| 
 | ||||
| 		/// Sends transaction; will block waiting for signer to return the
 | ||||
| 		/// transaction hash.
 | ||||
|  | ||||
| @ -27,8 +27,8 @@ build_rpc_trait! { | ||||
| 
 | ||||
| 		/// Posts sign request asynchronously.
 | ||||
| 		/// Will return a confirmation ID for later use with check_transaction.
 | ||||
| 		#[rpc(async, name = "parity_postSign")] | ||||
| 		fn post_sign(&self, H160, Bytes) -> BoxFuture<Either<U256, ConfirmationResponse>, Error>; | ||||
| 		#[rpc(meta, name = "parity_postSign")] | ||||
| 		fn post_sign(&self, Self::Metadata, H160, Bytes) -> BoxFuture<Either<U256, ConfirmationResponse>, Error>; | ||||
| 
 | ||||
| 		/// Posts transaction asynchronously.
 | ||||
| 		/// Will return a transaction ID for later use with check_transaction.
 | ||||
| @ -42,7 +42,7 @@ build_rpc_trait! { | ||||
| 
 | ||||
| 		/// Decrypt some ECIES-encrypted message.
 | ||||
| 		/// First parameter is the address with which it is encrypted, second is the ciphertext.
 | ||||
| 		#[rpc(async, name = "parity_decryptMessage")] | ||||
| 		fn decrypt_message(&self, H160, Bytes) -> BoxFuture<Bytes, Error>; | ||||
| 		#[rpc(meta, name = "parity_decryptMessage")] | ||||
| 		fn decrypt_message(&self, Self::Metadata, H160, Bytes) -> BoxFuture<Bytes, Error>; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -21,16 +21,19 @@ use serde::{Serialize, Serializer}; | ||||
| use util::log::Colour; | ||||
| use util::bytes::ToPretty; | ||||
| 
 | ||||
| use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, TransactionCondition}; | ||||
| use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, TransactionCondition, Origin}; | ||||
| use v1::helpers; | ||||
| 
 | ||||
| /// Confirmation waiting in a queue
 | ||||
| #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] | ||||
| #[serde(deny_unknown_fields)] | ||||
| pub struct ConfirmationRequest { | ||||
| 	/// Id of this confirmation
 | ||||
| 	pub id: U256, | ||||
| 	/// Payload
 | ||||
| 	pub payload: ConfirmationPayload, | ||||
| 	/// Request origin
 | ||||
| 	pub origin: Origin, | ||||
| } | ||||
| 
 | ||||
| impl From<helpers::ConfirmationRequest> for ConfirmationRequest { | ||||
| @ -38,13 +41,14 @@ impl From<helpers::ConfirmationRequest> for ConfirmationRequest { | ||||
| 		ConfirmationRequest { | ||||
| 			id: c.id.into(), | ||||
| 			payload: c.payload.into(), | ||||
| 			origin: c.origin, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for ConfirmationRequest { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		write!(f, "#{}: {}", self.id, self.payload) | ||||
| 		write!(f, "#{}: {} coming from {}", self.id, self.payload, self.origin) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -61,6 +65,7 @@ impl fmt::Display for ConfirmationPayload { | ||||
| 
 | ||||
| /// Sign request
 | ||||
| #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] | ||||
| #[serde(deny_unknown_fields)] | ||||
| pub struct SignRequest { | ||||
| 	/// Address
 | ||||
| 	pub address: H160, | ||||
| @ -90,6 +95,7 @@ impl fmt::Display for SignRequest { | ||||
| 
 | ||||
| /// Decrypt request
 | ||||
| #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] | ||||
| #[serde(deny_unknown_fields)] | ||||
| pub struct DecryptRequest { | ||||
| 	/// Address
 | ||||
| 	pub address: H160, | ||||
| @ -153,6 +159,7 @@ pub struct ConfirmationResponseWithToken { | ||||
| 
 | ||||
| /// Confirmation payload, i.e. the thing to be confirmed
 | ||||
| #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] | ||||
| #[serde(deny_unknown_fields)] | ||||
| pub enum ConfirmationPayload { | ||||
| 	/// Send Transaction
 | ||||
| 	#[serde(rename="sendTransaction")] | ||||
| @ -249,11 +256,12 @@ mod tests { | ||||
| 		let request = helpers::ConfirmationRequest { | ||||
| 			id: 15.into(), | ||||
| 			payload: helpers::ConfirmationPayload::Signature(1.into(), vec![5].into()), | ||||
| 			origin: Origin::Rpc("test service".into()), | ||||
| 		}; | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res = serde_json::to_string(&ConfirmationRequest::from(request)); | ||||
| 		let expected = r#"{"id":"0xf","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#; | ||||
| 		let expected = r#"{"id":"0xf","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}},"origin":{"rpc":"test service"}}"#; | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res.unwrap(), expected.to_owned()); | ||||
| @ -275,11 +283,12 @@ mod tests { | ||||
| 				nonce: Some(1.into()), | ||||
| 				condition: None, | ||||
| 			}), | ||||
| 			origin: Origin::Signer(5.into()), | ||||
| 		}; | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res = serde_json::to_string(&ConfirmationRequest::from(request)); | ||||
| 		let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}}}"#; | ||||
| 		let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}},"origin":{"signer":"0x0000000000000000000000000000000000000000000000000000000000000005"}}"#; | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res.unwrap(), expected.to_owned()); | ||||
| @ -301,11 +310,12 @@ mod tests { | ||||
| 				nonce: Some(1.into()), | ||||
| 				condition: None, | ||||
| 			}), | ||||
| 			origin: Origin::Dapps("http://parity.io".into()), | ||||
| 		}; | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res = serde_json::to_string(&ConfirmationRequest::from(request)); | ||||
| 		let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}}}"#; | ||||
| 		let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}},"origin":{"dapp":"http://parity.io"}}"#; | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res.unwrap(), expected.to_owned()); | ||||
| @ -319,11 +329,12 @@ mod tests { | ||||
| 			payload: helpers::ConfirmationPayload::Decrypt( | ||||
| 				10.into(), vec![1, 2, 3].into(), | ||||
| 			), | ||||
| 			origin: Default::default(), | ||||
| 		}; | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res = serde_json::to_string(&ConfirmationRequest::from(request)); | ||||
| 		let expected = r#"{"id":"0xf","payload":{"decrypt":{"address":"0x000000000000000000000000000000000000000a","msg":"0x010203"}}}"#; | ||||
| 		let expected = r#"{"id":"0xf","payload":{"decrypt":{"address":"0x000000000000000000000000000000000000000a","msg":"0x010203"}},"origin":"unknown"}"#; | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res.unwrap(), expected.to_owned()); | ||||
|  | ||||
| @ -1,80 +0,0 @@ | ||||
| // 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/>.
 | ||||
| 
 | ||||
| //! Dapp Id type
 | ||||
| 
 | ||||
| use ethcore::account_provider::DappId as EthDappId; | ||||
| 
 | ||||
| /// Dapplication Internal Id
 | ||||
| #[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)] | ||||
| pub struct DappId(pub String); | ||||
| 
 | ||||
| impl Into<String> for DappId { | ||||
| 	fn into(self) -> String { | ||||
| 		self.0 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From<String> for DappId { | ||||
| 	fn from(s: String) -> Self { | ||||
| 		DappId(s) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From<EthDappId> for DappId { | ||||
| 	fn from(id: EthDappId) -> Self { | ||||
| 		DappId(id.into()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Into<EthDappId> for DappId { | ||||
| 	fn into(self) -> EthDappId { | ||||
| 		Into::<String>::into(self).into() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| 
 | ||||
| 	use serde_json; | ||||
| 	use super::DappId; | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn should_serialize_dapp_id() { | ||||
| 		// given
 | ||||
| 		let id = DappId("testapp".into()); | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res = serde_json::to_string(&id).unwrap(); | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res, r#""testapp""#); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn should_deserialize_dapp_id() { | ||||
| 		// given
 | ||||
| 		let id = r#""testapp""#; | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res: DappId = serde_json::from_str(id).unwrap(); | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res, DappId("testapp".into())); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -36,11 +36,18 @@ macro_rules! impl_hash { | ||||
| 		} | ||||
| 
 | ||||
| 		impl fmt::Debug for $name { | ||||
| 			fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { | ||||
| 			fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 				write!(f, "{}", self.0.to_hex()) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		impl fmt::Display for $name { | ||||
| 			fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 				let hex = self.0.to_hex(); | ||||
| 				write!(f, "{}..{}", &hex[0..2], &hex[$size-2..$size]) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		impl<T> From<T> for $name where $other: From<T> { | ||||
| 			fn from(o: T) -> Self { | ||||
| 				$name($other::from(o).0) | ||||
|  | ||||
| @ -18,29 +18,30 @@ | ||||
| //! RPC types
 | ||||
| 
 | ||||
| mod account_info; | ||||
| mod bytes; | ||||
| mod block; | ||||
| mod block_number; | ||||
| mod bytes; | ||||
| mod call_request; | ||||
| mod confirmations; | ||||
| mod dapp_id; | ||||
| mod consensus_status; | ||||
| mod filter; | ||||
| mod hash; | ||||
| mod histogram; | ||||
| mod index; | ||||
| mod log; | ||||
| mod provenance; | ||||
| mod receipt; | ||||
| mod rpc_settings; | ||||
| mod sync; | ||||
| mod trace; | ||||
| mod trace_filter; | ||||
| mod transaction; | ||||
| mod transaction_request; | ||||
| mod transaction_condition; | ||||
| mod receipt; | ||||
| mod rpc_settings; | ||||
| mod trace; | ||||
| mod trace_filter; | ||||
| mod uint; | ||||
| mod work; | ||||
| mod histogram; | ||||
| mod consensus_status; | ||||
| 
 | ||||
| pub use self::account_info::{AccountInfo, HwAccountInfo}; | ||||
| pub use self::bytes::Bytes; | ||||
| pub use self::block::{RichBlock, Block, BlockTransactions}; | ||||
| pub use self::block_number::BlockNumber; | ||||
| @ -49,24 +50,24 @@ pub use self::confirmations::{ | ||||
| 	ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, | ||||
| 	TransactionModification, SignRequest, DecryptRequest, Either | ||||
| }; | ||||
| pub use self::dapp_id::DappId; | ||||
| pub use self::consensus_status::*; | ||||
| pub use self::filter::{Filter, FilterChanges}; | ||||
| pub use self::hash::{H64, H160, H256, H512, H520, H2048}; | ||||
| pub use self::histogram::Histogram; | ||||
| pub use self::index::Index; | ||||
| pub use self::log::Log; | ||||
| pub use self::provenance::{Origin, DappId}; | ||||
| pub use self::receipt::Receipt; | ||||
| pub use self::rpc_settings::RpcSettings; | ||||
| pub use self::sync::{ | ||||
| 	SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, | ||||
| 	TransactionStats, ChainStatus, EthProtocolInfo, LesProtocolInfo, | ||||
| }; | ||||
| pub use self::trace::{LocalizedTrace, TraceResults}; | ||||
| pub use self::trace_filter::TraceFilter; | ||||
| pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; | ||||
| pub use self::transaction_request::TransactionRequest; | ||||
| pub use self::transaction_condition::TransactionCondition; | ||||
| pub use self::receipt::Receipt; | ||||
| pub use self::rpc_settings::RpcSettings; | ||||
| pub use self::trace::{LocalizedTrace, TraceResults}; | ||||
| pub use self::trace_filter::TraceFilter; | ||||
| pub use self::uint::{U128, U256}; | ||||
| pub use self::work::Work; | ||||
| pub use self::histogram::Histogram; | ||||
| pub use self::consensus_status::*; | ||||
| pub use self::account_info::{AccountInfo, HwAccountInfo}; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										154
									
								
								rpc/src/v1/types/provenance.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								rpc/src/v1/types/provenance.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,154 @@ | ||||
| // 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/>.
 | ||||
| 
 | ||||
| //! Request Provenance
 | ||||
| 
 | ||||
| use std::fmt; | ||||
| use ethcore::account_provider::DappId as EthDappId; | ||||
| use v1::types::H256; | ||||
| 
 | ||||
| /// RPC request origin
 | ||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||||
| #[serde(deny_unknown_fields)] | ||||
| pub enum Origin { | ||||
| 	/// RPC server (includes request origin)
 | ||||
| 	#[serde(rename="rpc")] | ||||
| 	Rpc(String), | ||||
| 	/// Dapps server (includes DappId)
 | ||||
| 	#[serde(rename="dapp")] | ||||
| 	Dapps(DappId), | ||||
| 	/// IPC server (includes session hash)
 | ||||
| 	#[serde(rename="ipc")] | ||||
| 	Ipc(H256), | ||||
| 	/// Signer (includes session hash)
 | ||||
| 	#[serde(rename="signer")] | ||||
| 	Signer(H256), | ||||
| 	/// Unknown
 | ||||
| 	#[serde(rename="unknown")] | ||||
| 	Unknown, | ||||
| } | ||||
| 
 | ||||
| impl Default for Origin { | ||||
| 	fn default() -> Self { | ||||
| 		Origin::Unknown | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl fmt::Display for Origin { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		match *self { | ||||
| 			Origin::Rpc(ref origin) => write!(f, "RPC (service: {})", origin), | ||||
| 			Origin::Dapps(ref origin) => write!(f, "Dapp {}", origin), | ||||
| 			Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session), | ||||
| 			Origin::Signer(ref session) => write!(f, "UI (session: {})", session), | ||||
| 			Origin::Unknown => write!(f, "unknown origin"), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /// Dapplication Internal Id
 | ||||
| #[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)] | ||||
| pub struct DappId(pub String); | ||||
| 
 | ||||
| impl fmt::Display for DappId { | ||||
| 	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
| 		write!(f, "{}", self.0) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Into<String> for DappId { | ||||
| 	fn into(self) -> String { | ||||
| 		self.0 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From<String> for DappId { | ||||
| 	fn from(s: String) -> Self { | ||||
| 		DappId(s) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl<'a> From<&'a str> for DappId { | ||||
| 	fn from(s: &'a str) -> Self { | ||||
| 		DappId(s.to_owned()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl From<EthDappId> for DappId { | ||||
| 	fn from(id: EthDappId) -> Self { | ||||
| 		DappId(id.into()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Into<EthDappId> for DappId { | ||||
| 	fn into(self) -> EthDappId { | ||||
| 		Into::<String>::into(self).into() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| 	use serde_json; | ||||
| 	use super::{DappId, Origin}; | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn should_serialize_origin() { | ||||
| 		// given
 | ||||
| 		let o1 = Origin::Rpc("test service".into()); | ||||
| 		let o2 = Origin::Dapps("http://parity.io".into()); | ||||
| 		let o3 = Origin::Ipc(5.into()); | ||||
| 		let o4 = Origin::Signer(10.into()); | ||||
| 		let o5 = Origin::Unknown; | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res1 = serde_json::to_string(&o1).unwrap(); | ||||
| 		let res2 = serde_json::to_string(&o2).unwrap(); | ||||
| 		let res3 = serde_json::to_string(&o3).unwrap(); | ||||
| 		let res4 = serde_json::to_string(&o4).unwrap(); | ||||
| 		let res5 = serde_json::to_string(&o5).unwrap(); | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res1, r#"{"rpc":"test service"}"#); | ||||
| 		assert_eq!(res2, r#"{"dapp":"http://parity.io"}"#); | ||||
| 		assert_eq!(res3, r#"{"ipc":"0x0000000000000000000000000000000000000000000000000000000000000005"}"#); | ||||
| 		assert_eq!(res4, r#"{"signer":"0x000000000000000000000000000000000000000000000000000000000000000a"}"#); | ||||
| 		assert_eq!(res5, r#""unknown""#); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn should_serialize_dapp_id() { | ||||
| 		// given
 | ||||
| 		let id = DappId("testapp".into()); | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res = serde_json::to_string(&id).unwrap(); | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res, r#""testapp""#); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn should_deserialize_dapp_id() { | ||||
| 		// given
 | ||||
| 		let id = r#""testapp""#; | ||||
| 
 | ||||
| 		// when
 | ||||
| 		let res: DappId = serde_json::from_str(id).unwrap(); | ||||
| 
 | ||||
| 		// then
 | ||||
| 		assert_eq!(res, DappId("testapp".into())); | ||||
| 	} | ||||
| } | ||||
| @ -18,6 +18,7 @@ use ethcore; | ||||
| 
 | ||||
| /// Represents condition on minimum block number or block timestamp.
 | ||||
| #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] | ||||
| #[serde(deny_unknown_fields)] | ||||
| pub enum TransactionCondition { | ||||
| 	/// Valid at this minimum block number.
 | ||||
| 	#[serde(rename="block")] | ||||
|  | ||||
| @ -18,6 +18,9 @@ extern crate log; | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| 	#[macro_use] | ||||
| 	extern crate matches; | ||||
| 
 | ||||
| 	use futures::Future; | ||||
| 	use std::path::PathBuf; | ||||
| 	use client::{Rpc, RpcError}; | ||||
|  | ||||
| @ -33,6 +33,8 @@ use rpc::informant::RpcStats; | ||||
| 
 | ||||
| mod session; | ||||
| 
 | ||||
| pub use self::session::MetaExtractor; | ||||
| 
 | ||||
| /// Signer startup error
 | ||||
| #[derive(Debug)] | ||||
| pub enum ServerError { | ||||
| @ -51,6 +53,11 @@ impl From<ws::Error> for ServerError { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /// Dummy metadata extractor
 | ||||
| #[derive(Clone)] | ||||
| pub struct NoopExtractor; | ||||
| impl<M: Metadata> session::MetaExtractor<M> for NoopExtractor {} | ||||
| 
 | ||||
| /// Builder for `WebSockets` server
 | ||||
| pub struct ServerBuilder { | ||||
| 	queue: Arc<ConfirmationsQueue>, | ||||
| @ -86,6 +93,17 @@ impl ServerBuilder { | ||||
| 	/// Starts a new `WebSocket` server in separate thread.
 | ||||
| 	/// Returns a `Server` handle which closes the server when droped.
 | ||||
| 	pub fn start<M: Metadata, S: Middleware<M>>(self, addr: SocketAddr, handler: RpcHandler<M, S>) -> Result<Server, ServerError> { | ||||
| 		self.start_with_extractor(addr, handler, NoopExtractor) | ||||
| 	} | ||||
| 
 | ||||
| 	/// Starts a new `WebSocket` server in separate thread.
 | ||||
| 	/// Returns a `Server` handle which closes the server when droped.
 | ||||
| 	pub fn start_with_extractor<M: Metadata, S: Middleware<M>, T: session::MetaExtractor<M>>( | ||||
| 		self, | ||||
| 		addr: SocketAddr, | ||||
| 		handler: RpcHandler<M, S>, | ||||
| 		meta_extractor: T, | ||||
| 	) -> Result<Server, ServerError> { | ||||
| 		Server::start( | ||||
| 			addr, | ||||
| 			handler, | ||||
| @ -93,8 +111,10 @@ impl ServerBuilder { | ||||
| 			self.authcodes_path, | ||||
| 			self.skip_origin_validation, | ||||
| 			self.stats, | ||||
| 			meta_extractor, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /// `WebSockets` server implementation.
 | ||||
| @ -114,13 +134,14 @@ impl Server { | ||||
| 
 | ||||
| 	/// Starts a new `WebSocket` server in separate thread.
 | ||||
| 	/// Returns a `Server` handle which closes the server when droped.
 | ||||
| 	fn start<M: Metadata, S: Middleware<M>>( | ||||
| 	fn start<M: Metadata, S: Middleware<M>, T: session::MetaExtractor<M>>( | ||||
| 		addr: SocketAddr, | ||||
| 		handler: RpcHandler<M, S>, | ||||
| 		queue: Arc<ConfirmationsQueue>, | ||||
| 		authcodes_path: PathBuf, | ||||
| 		skip_origin_validation: bool, | ||||
| 		stats: Option<Arc<RpcStats>>, | ||||
| 		meta_extractor: T, | ||||
| 	) -> Result<Server, ServerError> { | ||||
| 		let config = { | ||||
| 			let mut config = ws::Settings::default(); | ||||
| @ -135,7 +156,7 @@ impl Server { | ||||
| 		let origin = format!("{}", addr); | ||||
| 		let port = addr.port(); | ||||
| 		let ws = ws::Builder::new().with_settings(config).build( | ||||
| 			session::Factory::new(handler, origin, port, authcodes_path, skip_origin_validation, stats) | ||||
| 			session::Factory::new(handler, origin, port, authcodes_path, skip_origin_validation, stats, meta_extractor) | ||||
| 		)?; | ||||
| 
 | ||||
| 		let panic_handler = PanicHandler::new_in_arc(); | ||||
|  | ||||
| @ -16,15 +16,16 @@ | ||||
| 
 | ||||
| //! Session handlers factory.
 | ||||
| 
 | ||||
| use ws; | ||||
| use authcode_store::AuthCodes; | ||||
| use std::path::{PathBuf, Path}; | ||||
| use std::sync::Arc; | ||||
| use std::str::FromStr; | ||||
| 
 | ||||
| use authcode_store::AuthCodes; | ||||
| use jsonrpc_core::{Metadata, Middleware}; | ||||
| use jsonrpc_core::reactor::RpcHandler; | ||||
| use rpc::informant::RpcStats; | ||||
| use util::{H256, version}; | ||||
| use ws; | ||||
| 
 | ||||
| #[cfg(feature = "parity-ui")] | ||||
| mod ui { | ||||
| @ -78,10 +79,10 @@ fn origin_is_allowed(self_origin: &str, header: Option<&[u8]>) -> bool { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn auth_is_valid(codes_path: &Path, protocols: ws::Result<Vec<&str>>) -> bool { | ||||
| fn auth_token_hash(codes_path: &Path, protocols: ws::Result<Vec<&str>>) -> Option<H256> { | ||||
| 	match protocols { | ||||
| 		Ok(ref protocols) if protocols.len() == 1 => { | ||||
| 			protocols.iter().any(|protocol| { | ||||
| 			let protocol = protocols[0]; | ||||
| 			let mut split = protocol.split('_'); | ||||
| 			let auth = split.next().and_then(|v| H256::from_str(v).ok()); | ||||
| 			let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok()); | ||||
| @ -89,7 +90,8 @@ fn auth_is_valid(codes_path: &Path, protocols: ws::Result<Vec<&str>>) -> bool { | ||||
| 			if let (Some(auth), Some(time)) = (auth, time) { | ||||
| 				// Check if the code is valid
 | ||||
| 				AuthCodes::from_file(codes_path) | ||||
| 						.map(|mut codes| { | ||||
| 					.ok() | ||||
| 					.and_then(|mut codes| { | ||||
| 						// remove old tokens
 | ||||
| 						codes.clear_garbage(); | ||||
| 
 | ||||
| @ -98,15 +100,18 @@ fn auth_is_valid(codes_path: &Path, protocols: ws::Result<Vec<&str>>) -> bool { | ||||
| 						if codes.to_file(codes_path).is_err() { | ||||
| 							warn!(target: "signer", "Couldn't save authorization codes to file."); | ||||
| 						} | ||||
| 							res | ||||
| 						}) | ||||
| 						.unwrap_or(false) | ||||
| 
 | ||||
| 						if res { | ||||
| 							Some(auth) | ||||
| 						} else { | ||||
| 					false | ||||
| 							None | ||||
| 						} | ||||
| 					}) | ||||
| 			} else { | ||||
| 				None | ||||
| 			} | ||||
| 		}, | ||||
| 		_ => false | ||||
| 		_ => None, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -125,7 +130,16 @@ fn add_headers(mut response: ws::Response, mime: &str) -> ws::Response { | ||||
| 	response | ||||
| } | ||||
| 
 | ||||
| pub struct Session<M: Metadata, S: Middleware<M>> { | ||||
| /// Metadata extractor from session data.
 | ||||
| pub trait MetaExtractor<M: Metadata>: Send + Clone + 'static { | ||||
| 	/// Extract metadata for given session
 | ||||
| 	fn extract_metadata(&self, _session_id: &H256) -> M { | ||||
| 		Default::default() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub struct Session<M: Metadata, S: Middleware<M>, T> { | ||||
| 	session_id: H256, | ||||
| 	out: ws::Sender, | ||||
| 	skip_origin_validation: bool, | ||||
| 	self_origin: String, | ||||
| @ -134,16 +148,16 @@ pub struct Session<M: Metadata, S: Middleware<M>> { | ||||
| 	handler: RpcHandler<M, S>, | ||||
| 	file_handler: Arc<ui::Handler>, | ||||
| 	stats: Option<Arc<RpcStats>>, | ||||
| 	meta_extractor: T, | ||||
| } | ||||
| 
 | ||||
| impl<M: Metadata, S: Middleware<M>> Drop for Session<M, S> { | ||||
| impl<M: Metadata, S: Middleware<M>, T> Drop for Session<M, S, T> { | ||||
| 	fn drop(&mut self) { | ||||
| 		self.stats.as_ref().map(|stats| stats.close_session()); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl<M: Metadata, S: Middleware<M>> ws::Handler for Session<M, S> { | ||||
| 	#[cfg_attr(feature="dev", allow(collapsible_if))] | ||||
| impl<M: Metadata, S: Middleware<M>, T: MetaExtractor<M>> ws::Handler for Session<M, S, T> { | ||||
| 	fn on_request(&mut self, req: &ws::Request) -> ws::Result<(ws::Response)> { | ||||
| 		trace!(target: "signer", "Handling request: {:?}", req); | ||||
| 
 | ||||
| @ -186,9 +200,15 @@ impl<M: Metadata, S: Middleware<M>> ws::Handler for Session<M, S> { | ||||
| 		// (styles file skips origin validation, so make sure to prevent WS connections on this resource)
 | ||||
| 		if req.header("sec-websocket-key").is_some() && !is_styles_file { | ||||
| 			// Check authorization
 | ||||
| 			if !auth_is_valid(&self.authcodes_path, req.protocols()) { | ||||
| 			let auth_token_hash = auth_token_hash(&self.authcodes_path, req.protocols()); | ||||
| 			match auth_token_hash { | ||||
| 				None => { | ||||
| 					info!(target: "signer", "Unauthorized connection to Signer API blocked."); | ||||
| 					return Ok(error(ErrorType::Forbidden, "Not Authorized", "Request to this API was not authorized.", None)); | ||||
| 				}, | ||||
| 				Some(auth) => { | ||||
| 					self.session_id = auth; | ||||
| 				}, | ||||
| 			} | ||||
| 
 | ||||
| 			let protocols = req.protocols().expect("Existence checked by authorization."); | ||||
| @ -214,8 +234,8 @@ impl<M: Metadata, S: Middleware<M>> ws::Handler for Session<M, S> { | ||||
| 	fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { | ||||
| 		let req = msg.as_text()?; | ||||
| 		let out = self.out.clone(); | ||||
| 		// TODO [ToDr] Extract metadata for PubSub/Session
 | ||||
| 		let metadata = Default::default(); | ||||
| 		// TODO [ToDr] Move to on_connect
 | ||||
| 		let metadata = self.meta_extractor.extract_metadata(&self.session_id); | ||||
| 
 | ||||
| 		self.handler.handle_request(req, metadata, move |response| { | ||||
| 			if let Some(result) = response { | ||||
| @ -229,17 +249,18 @@ impl<M: Metadata, S: Middleware<M>> ws::Handler for Session<M, S> { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub struct Factory<M: Metadata, S: Middleware<M>> { | ||||
| pub struct Factory<M: Metadata, S: Middleware<M>, T> { | ||||
| 	handler: RpcHandler<M, S>, | ||||
| 	skip_origin_validation: bool, | ||||
| 	self_origin: String, | ||||
| 	self_port: u16, | ||||
| 	authcodes_path: PathBuf, | ||||
| 	meta_extractor: T, | ||||
| 	file_handler: Arc<ui::Handler>, | ||||
| 	stats: Option<Arc<RpcStats>>, | ||||
| } | ||||
| 
 | ||||
| impl<M: Metadata, S: Middleware<M>> Factory<M, S> { | ||||
| impl<M: Metadata, S: Middleware<M>, T> Factory<M, S, T> { | ||||
| 	pub fn new( | ||||
| 		handler: RpcHandler<M, S>, | ||||
| 		self_origin: String, | ||||
| @ -247,6 +268,7 @@ impl<M: Metadata, S: Middleware<M>> Factory<M, S> { | ||||
| 		authcodes_path: PathBuf, | ||||
| 		skip_origin_validation: bool, | ||||
| 		stats: Option<Arc<RpcStats>>, | ||||
| 		meta_extractor: T, | ||||
| 	) -> Self { | ||||
| 		Factory { | ||||
| 			handler: handler, | ||||
| @ -254,25 +276,28 @@ impl<M: Metadata, S: Middleware<M>> Factory<M, S> { | ||||
| 			self_origin: self_origin, | ||||
| 			self_port: self_port, | ||||
| 			authcodes_path: authcodes_path, | ||||
| 			meta_extractor: meta_extractor, | ||||
| 			file_handler: Arc::new(ui::Handler::default()), | ||||
| 			stats: stats, | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl<M: Metadata, S: Middleware<M>> ws::Factory for Factory<M, S> { | ||||
| 	type Handler = Session<M, S>; | ||||
| impl<M: Metadata, S: Middleware<M>, T: MetaExtractor<M>> ws::Factory for Factory<M, S, T> { | ||||
| 	type Handler = Session<M, S, T>; | ||||
| 
 | ||||
| 	fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { | ||||
| 		self.stats.as_ref().map(|stats| stats.open_session()); | ||||
| 
 | ||||
| 		Session { | ||||
| 			session_id: 0.into(), | ||||
| 			out: sender, | ||||
| 			handler: self.handler.clone(), | ||||
| 			skip_origin_validation: self.skip_origin_validation, | ||||
| 			self_origin: self.self_origin.clone(), | ||||
| 			self_port: self.self_port, | ||||
| 			authcodes_path: self.authcodes_path.clone(), | ||||
| 			meta_extractor: self.meta_extractor.clone(), | ||||
| 			file_handler: self.file_handler.clone(), | ||||
| 			stats: self.stats.clone(), | ||||
| 		} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user