UI server refactoring (#5580)

* Full API in Authenticated WS server.

* Replacing UI server with Hyper.

* Solving CLI, RPCs and tests.

* Porting signer tests.

* Fixing origin recognition for dapps/rpc.

* Fixing tests. Adding parity-rpc-client to test.

* Dapps exposed as RPC method.

* JS code to support new connection scheme.

* Fixing dapps tests.

* Updating allowed origins/hosts to support web3.site.

* Fixing tests, fixing UI.

* Fixing tests.

* Removing invalid tests.

* Fixing merge.

* 404 fallback for UI

* Improve ContentFetcher constructor readability.

* Naming.

* Update .gitlab-ci.yml

fix CI lint error

* Fixing tests and linting issues.

* Fixing new tests.

* UI hosts.

* Submodules fix.
This commit is contained in:
Tomasz Drwięga
2017-05-24 12:24:07 +02:00
committed by Arkadiy Paronyan
parent 7499efecf6
commit cbcc369a2d
91 changed files with 2171 additions and 2591 deletions

View File

@@ -124,6 +124,8 @@ usage! {
or |c: &Config| otry!(c.ui).port.clone(),
flag_ui_interface: String = "local",
or |c: &Config| otry!(c.ui).interface.clone(),
flag_ui_hosts: String = "none",
or |c: &Config| otry!(c.ui).hosts.as_ref().map(|vec| vec.join(",")),
flag_ui_path: String = "$BASE/signer",
or |c: &Config| otry!(c.ui).path.clone(),
// NOTE [todr] For security reasons don't put this to config files
@@ -188,7 +190,7 @@ usage! {
or |c: &Config| otry!(c.websockets).interface.clone(),
flag_ws_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore",
or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")),
flag_ws_origins: String = "none",
flag_ws_origins: String = "chrome-extension://*",
or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")),
flag_ws_hosts: String = "none",
or |c: &Config| otry!(c.websockets).hosts.as_ref().map(|vec| vec.join(",")),
@@ -430,6 +432,7 @@ struct Ui {
disable: Option<bool>,
port: Option<u16>,
interface: Option<String>,
hosts: Option<Vec<String>>,
path: Option<String>,
}
@@ -709,6 +712,7 @@ mod tests {
flag_no_ui: false,
flag_ui_port: 8180u16,
flag_ui_interface: "127.0.0.1".into(),
flag_ui_hosts: "none".into(),
flag_ui_path: "$HOME/.parity/signer".into(),
flag_ui_no_validation: false,
@@ -929,6 +933,7 @@ mod tests {
disable: Some(true),
port: None,
interface: None,
hosts: None,
path: None,
}),
network: Some(Network {

View File

@@ -110,6 +110,11 @@ UI Options:
--ui-interface IP Specify the hostname portion of the Trusted UI
server, IP should be an interface's IP address,
or local (default: {flag_ui_interface}).
--ui-hosts HOSTS List of allowed Host header values. This option will
validate the Host header sent by the browser, it
is additional security against some attack
vectors. Special options: "all", "none",
(default: {flag_ui_hosts}).
--ui-path PATH Specify directory where Trusted UIs tokens should
be stored. (default: {flag_ui_path})
--ui-no-validation Disable Origin and Host headers validation for

View File

@@ -30,7 +30,7 @@ use ethcore::client::{VMType};
use ethcore::miner::{MinerOptions, Banning, StratumOptions};
use ethcore::verification::queue::VerifierSettings;
use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration};
use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration, UiConfiguration};
use rpc_apis::ApiSet;
use parity_rpc::NetworkSettings;
use cache::CacheConfig;
@@ -41,7 +41,6 @@ use ethcore_logger::Config as LogConfig;
use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path};
use dapps::Configuration as DappsConfiguration;
use ipfs::Configuration as IpfsConfiguration;
use signer::{Configuration as SignerConfiguration};
use secretstore::Configuration as SecretStoreConfiguration;
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
use run::RunCmd;
@@ -50,8 +49,6 @@ use presale::ImportWallet;
use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts};
use snapshot::{self, SnapshotCommand};
const AUTHCODE_FILENAME: &'static str = "authcodes";
#[derive(Debug, PartialEq)]
pub enum Cmd {
Run(RunCmd),
@@ -59,7 +56,7 @@ pub enum Cmd {
Account(AccountCmd),
ImportPresaleWallet(ImportWallet),
Blockchain(BlockchainCmd),
SignerToken(SignerConfiguration),
SignerToken(WsConfiguration, UiConfiguration),
SignerSign {
id: Option<usize>,
pwfile: Option<PathBuf>,
@@ -118,6 +115,7 @@ impl Configuration {
let http_conf = self.http_config()?;
let ipc_conf = self.ipc_config()?;
let net_conf = self.net_config()?;
let ui_conf = self.ui_config();
let network_id = self.network_id();
let cache_config = self.cache_config();
let tracing = self.args.flag_tracing.parse()?;
@@ -134,10 +132,8 @@ impl Configuration {
let public_node = self.args.flag_public_node;
let warp_sync = !self.args.flag_no_warp && fat_db != Switch::On && tracing != Switch::On && pruning != Pruning::Specific(Algorithm::Archive);
let geth_compatibility = self.args.flag_geth;
let ui_address = self.ui_port().map(|port| (self.ui_interface(), port));
let mut dapps_conf = self.dapps_config();
let ipfs_conf = self.ipfs_config();
let signer_conf = self.signer_config();
let secretstore_conf = self.secretstore_config()?;
let format = self.format()?;
@@ -149,11 +145,10 @@ impl Configuration {
let cmd = if self.args.flag_version {
Cmd::Version
} else if self.args.cmd_signer {
let mut authfile = PathBuf::from(signer_conf.signer_path.clone());
authfile.push(AUTHCODE_FILENAME);
let authfile = ::signer::codes_path(&ws_conf.signer_path);
if self.args.cmd_new_token {
Cmd::SignerToken(signer_conf)
Cmd::SignerToken(ws_conf, ui_conf)
} else if self.args.cmd_sign {
let pwfile = self.args.flag_password.get(0).map(|pwfile| {
PathBuf::from(pwfile)
@@ -161,18 +156,18 @@ impl Configuration {
Cmd::SignerSign {
id: self.args.arg_id,
pwfile: pwfile,
port: signer_conf.port,
port: ws_conf.port,
authfile: authfile,
}
} else if self.args.cmd_reject {
Cmd::SignerReject {
id: self.args.arg_id,
port: signer_conf.port,
port: ws_conf.port,
authfile: authfile,
}
} else if self.args.cmd_list {
Cmd::SignerList {
port: signer_conf.port,
port: ws_conf.port,
authfile: authfile,
}
} else {
@@ -372,11 +367,10 @@ impl Configuration {
warp_sync: warp_sync,
public_node: public_node,
geth_compatibility: geth_compatibility,
ui_address: ui_address,
net_settings: self.network_settings()?,
dapps_conf: dapps_conf,
ipfs_conf: ipfs_conf,
signer_conf: signer_conf,
ui_conf: ui_conf,
secretstore_conf: secretstore_conf,
dapp: self.dapp_to_open()?,
ui: self.args.cmd_ui,
@@ -553,13 +547,12 @@ impl Configuration {
Ok(options)
}
fn signer_config(&self) -> SignerConfiguration {
SignerConfiguration {
fn ui_config(&self) -> UiConfiguration {
UiConfiguration {
enabled: self.ui_enabled(),
port: self.args.flag_ports_shift + self.args.flag_ui_port,
interface: self.ui_interface(),
signer_path: self.directories().signer,
skip_origin_validation: self.args.flag_unsafe_expose || self.args.flag_ui_no_validation,
port: self.args.flag_ports_shift + self.args.flag_ui_port,
hosts: self.ui_hosts(),
}
}
@@ -768,6 +761,14 @@ impl Configuration {
Some(hosts)
}
fn ui_hosts(&self) -> Option<Vec<String>> {
if self.args.flag_ui_no_validation {
return None;
}
self.hosts(&self.args.flag_ui_hosts, &self.ui_interface())
}
fn rpc_hosts(&self) -> Option<Vec<String>> {
self.hosts(&self.args.flag_jsonrpc_hosts, &self.rpc_interface())
}
@@ -825,13 +826,17 @@ impl Configuration {
}
fn ws_config(&self) -> Result<WsConfiguration, String> {
let ui = self.ui_config();
let conf = WsConfiguration {
enabled: self.ws_enabled(),
interface: self.ws_interface(),
port: self.args.flag_ports_shift + self.args.flag_ws_port,
apis: self.args.flag_ws_apis.parse()?,
hosts: self.ws_hosts(),
origins: self.ws_origins()
origins: self.ws_origins(),
signer_path: self.directories().signer.into(),
ui_address: ui.address(),
};
Ok(conf)
@@ -928,18 +933,6 @@ impl Configuration {
}
}
fn ui_port(&self) -> Option<u16> {
if !self.ui_enabled() {
None
} else {
Some(self.args.flag_ui_port)
}
}
fn ui_interface(&self) -> String {
self.interface(&self.args.flag_ui_interface)
}
fn interface(&self, interface: &str) -> String {
if self.args.flag_unsafe_expose {
return "0.0.0.0".into();
@@ -952,6 +945,11 @@ impl Configuration {
}.into()
}
fn ui_interface(&self) -> String {
self.interface(&self.args.flag_ui_interface)
}
fn rpc_interface(&self) -> String {
let rpc_interface = self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone());
self.interface(&rpc_interface)
@@ -1050,24 +1048,27 @@ impl Configuration {
#[cfg(test)]
mod tests {
use super::*;
use cli::Args;
use parity_rpc::NetworkSettings;
use ethcore::client::{VMType, BlockId};
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
use helpers::{default_network_config};
use run::RunCmd;
use dir::{Directories, default_hypervisor_path};
use signer::{Configuration as SignerConfiguration};
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat, ExportState};
use presale::ImportWallet;
use params::SpecType;
use account::{AccountCmd, NewAccount, ImportAccounts, ListAccounts};
use devtools::{RandomTempPath};
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
use std::io::Write;
use std::fs::{File, create_dir};
use devtools::{RandomTempPath};
use ethcore::client::{VMType, BlockId};
use ethcore::miner::{MinerOptions, PrioritizationStrategy};
use parity_rpc::NetworkSettings;
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
use account::{AccountCmd, NewAccount, ImportAccounts, ListAccounts};
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat, ExportState};
use cli::Args;
use dir::{Directories, default_hypervisor_path};
use helpers::{default_network_config};
use params::SpecType;
use presale::ImportWallet;
use rpc::{WsConfiguration, UiConfiguration};
use run::RunCmd;
use super::*;
#[derive(Debug, PartialEq)]
struct TestPasswordReader(&'static str);
@@ -1233,12 +1234,20 @@ mod tests {
let args = vec!["parity", "signer", "new-token"];
let conf = parse(&args);
let expected = Directories::default().signer;
assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(SignerConfiguration {
assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(WsConfiguration {
enabled: true,
interface: "127.0.0.1".into(),
port: 8546,
apis: ApiSet::UnsafeContext,
origins: Some(vec!["chrome-extension://*".into()]),
hosts: Some(vec![]),
signer_path: expected.into(),
ui_address: Some(("127.0.0.1".to_owned(), 8180)),
}, UiConfiguration {
enabled: true,
signer_path: expected,
interface: "127.0.0.1".into(),
port: 8180,
skip_origin_validation: false,
hosts: Some(vec![]),
}));
}
@@ -1273,11 +1282,10 @@ mod tests {
wal: true,
vm_type: Default::default(),
geth_compatibility: false,
ui_address: Some(("127.0.0.1".into(), 8180)),
net_settings: Default::default(),
dapps_conf: Default::default(),
ipfs_conf: Default::default(),
signer_conf: Default::default(),
ui_conf: Default::default(),
secretstore_conf: Default::default(),
ui: false,
dapp: None,
@@ -1457,7 +1465,7 @@ mod tests {
}
#[test]
fn should_parse_signer_configration() {
fn should_parse_ui_configuration() {
// given
// when
@@ -1467,33 +1475,33 @@ mod tests {
let conf3 = parse(&["parity", "--ui-path", "signer", "--ui-interface", "test"]);
// then
assert_eq!(conf0.signer_config(), SignerConfiguration {
assert_eq!(conf0.directories().signer, "signer".to_owned());
assert_eq!(conf0.ui_config(), UiConfiguration {
enabled: true,
port: 8180,
interface: "127.0.0.1".into(),
signer_path: "signer".into(),
skip_origin_validation: false,
});
assert_eq!(conf1.signer_config(), SignerConfiguration {
enabled: true,
port: 8180,
interface: "127.0.0.1".into(),
signer_path: "signer".into(),
skip_origin_validation: true,
hosts: Some(vec![]),
});
assert_eq!(conf2.signer_config(), SignerConfiguration {
assert_eq!(conf1.directories().signer, "signer".to_owned());
assert_eq!(conf1.ui_config(), UiConfiguration {
enabled: true,
interface: "127.0.0.1".into(),
port: 8180,
hosts: None,
});
assert_eq!(conf2.directories().signer, "signer".to_owned());
assert_eq!(conf2.ui_config(), UiConfiguration {
enabled: true,
interface: "127.0.0.1".into(),
port: 3123,
interface: "127.0.0.1".into(),
signer_path: "signer".into(),
skip_origin_validation: false,
hosts: Some(vec![]),
});
assert_eq!(conf3.signer_config(), SignerConfiguration {
assert_eq!(conf3.directories().signer, "signer".to_owned());
assert_eq!(conf3.ui_config(), UiConfiguration {
enabled: true,
port: 8180,
interface: "test".into(),
signer_path: "signer".into(),
skip_origin_validation: false,
port: 8180,
hosts: Some(vec![]),
});
}
@@ -1551,7 +1559,7 @@ mod tests {
assert_eq!(conf0.network_settings().unwrap().rpc_port, 8546);
assert_eq!(conf0.http_config().unwrap().port, 8546);
assert_eq!(conf0.ws_config().unwrap().port, 8547);
assert_eq!(conf0.signer_config().port, 8181);
assert_eq!(conf0.ui_config().port, 8181);
assert_eq!(conf0.secretstore_config().unwrap().port, 8084);
assert_eq!(conf0.secretstore_config().unwrap().http_port, 8083);
assert_eq!(conf0.ipfs_config().port, 5002);
@@ -1563,7 +1571,7 @@ mod tests {
assert_eq!(conf1.network_settings().unwrap().rpc_port, 8545);
assert_eq!(conf1.http_config().unwrap().port, 8545);
assert_eq!(conf1.ws_config().unwrap().port, 8547);
assert_eq!(conf1.signer_config().port, 8181);
assert_eq!(conf1.ui_config().port, 8181);
assert_eq!(conf1.secretstore_config().unwrap().port, 8084);
assert_eq!(conf1.secretstore_config().unwrap().http_port, 8083);
assert_eq!(conf1.ipfs_config().port, 5002);
@@ -1582,8 +1590,8 @@ mod tests {
assert_eq!(conf0.http_config().unwrap().hosts, None);
assert_eq!(&conf0.ws_config().unwrap().interface, "0.0.0.0");
assert_eq!(conf0.ws_config().unwrap().hosts, None);
assert_eq!(&conf0.signer_config().interface, "0.0.0.0");
assert_eq!(conf0.signer_config().skip_origin_validation, true);
assert_eq!(&conf0.ui_config().interface, "0.0.0.0");
assert_eq!(conf0.ui_config().hosts, None);
assert_eq!(&conf0.secretstore_config().unwrap().interface, "0.0.0.0");
assert_eq!(&conf0.secretstore_config().unwrap().http_interface, "0.0.0.0");
assert_eq!(&conf0.ipfs_config().interface, "0.0.0.0");

View File

@@ -27,6 +27,7 @@ use hash_fetch::urlhint::ContractClient;
use helpers::replace_home;
use light::client::Client as LightClient;
use light::on_demand::{self, OnDemand};
use rpc;
use rpc_apis::SignerService;
use parity_reactor;
use util::{Bytes, Address};
@@ -49,6 +50,15 @@ impl Default for Configuration {
}
}
impl Configuration {
pub fn address(&self, address: Option<(String, u16)>) -> Option<(String, u16)> {
match self.enabled {
true => address,
false => None,
}
}
}
/// Registrar implementation of the full client.
pub struct FullRegistrar {
/// Handle to the full client.
@@ -125,35 +135,49 @@ impl ContractClient for LightRegistrar {
// TODO: light client implementation forwarding to OnDemand and waiting for future
// to resolve.
#[derive(Clone)]
pub struct Dependencies {
pub sync_status: Arc<SyncStatus>,
pub contract_client: Arc<ContractClient>,
pub remote: parity_reactor::TokioRemote,
pub fetch: FetchClient,
pub signer: Arc<SignerService>,
pub ui_address: Option<(String, u16)>,
}
pub fn new(configuration: Configuration, deps: Dependencies)
-> Result<Option<Middleware>, String>
{
pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Middleware>, String> {
if !configuration.enabled {
return Ok(None);
}
dapps_middleware(
server::dapps_middleware(
deps,
configuration.dapps_path,
configuration.extra_dapps,
rpc::DAPPS_DOMAIN.into(),
).map(Some)
}
pub use self::server::{SyncStatus, Middleware, dapps_middleware};
pub fn new_ui(enabled: bool, deps: Dependencies) -> Result<Option<Middleware>, String> {
if !enabled {
return Ok(None);
}
server::ui_middleware(
deps,
rpc::DAPPS_DOMAIN.into(),
).map(Some)
}
pub use self::server::{SyncStatus, Middleware, service};
#[cfg(not(feature = "dapps"))]
mod server {
use super::Dependencies;
use std::sync::Arc;
use std::path::PathBuf;
use parity_rpc::{hyper, RequestMiddleware, RequestMiddlewareAction};
use rpc_apis;
pub type SyncStatus = Fn() -> bool;
@@ -170,9 +194,21 @@ mod server {
_deps: Dependencies,
_dapps_path: PathBuf,
_extra_dapps: Vec<PathBuf>,
_dapps_domain: String,
) -> Result<Middleware, String> {
Err("Your Parity version has been compiled without WebApps support.".into())
}
pub fn ui_middleware(
_deps: Dependencies,
_dapps_domain: String,
) -> Result<Middleware, String> {
Err("Your Parity version has been compiled without UI support.".into())
}
pub fn service(_: &Option<Middleware>) -> Option<Arc<rpc_apis::DappsService>> {
None
}
}
#[cfg(feature = "dapps")]
@@ -180,6 +216,7 @@ mod server {
use super::Dependencies;
use std::path::PathBuf;
use std::sync::Arc;
use rpc_apis;
use parity_dapps;
use parity_reactor;
@@ -191,20 +228,62 @@ mod server {
deps: Dependencies,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
dapps_domain: String,
) -> Result<Middleware, String> {
let signer = deps.signer.clone();
let signer = deps.signer;
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
let web_proxy_tokens = Arc::new(move |token| signer.is_valid_web_proxy_access_token(&token));
Ok(parity_dapps::Middleware::new(
Ok(parity_dapps::Middleware::dapps(
parity_remote,
deps.signer.address(),
deps.ui_address,
dapps_path,
extra_dapps,
dapps_domain,
deps.contract_client,
deps.sync_status,
web_proxy_tokens,
deps.fetch.clone(),
deps.fetch,
))
}
pub fn ui_middleware(
deps: Dependencies,
dapps_domain: String,
) -> Result<Middleware, String> {
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
Ok(parity_dapps::Middleware::ui(
parity_remote,
deps.contract_client,
deps.sync_status,
deps.fetch,
dapps_domain,
))
}
pub fn service(middleware: &Option<Middleware>) -> Option<Arc<rpc_apis::DappsService>> {
middleware.as_ref().map(|m| Arc::new(DappsServiceWrapper {
endpoints: m.endpoints()
}) as Arc<rpc_apis::DappsService>)
}
pub struct DappsServiceWrapper {
endpoints: parity_dapps::Endpoints,
}
impl rpc_apis::DappsService for DappsServiceWrapper {
fn list_dapps(&self) -> Vec<rpc_apis::LocalDapp> {
self.endpoints.list()
.into_iter()
.map(|app| rpc_apis::LocalDapp {
id: app.id,
name: app.name,
description: app.description,
version: app.version,
author: app.author,
icon_url: app.icon_url,
})
.collect()
}
}
}

View File

@@ -51,7 +51,6 @@ extern crate ethcore_ipc_hypervisor as hypervisor;
extern crate ethcore_ipc_nano as nanoipc;
extern crate ethcore_light as light;
extern crate ethcore_logger;
extern crate ethcore_signer;
extern crate ethcore_util as util;
extern crate ethkey;
extern crate ethsync;
@@ -114,9 +113,9 @@ mod presale;
mod rpc;
mod rpc_apis;
mod run;
mod secretstore;
mod signer;
mod snapshot;
mod secretstore;
mod upgrade;
mod url;
mod user_defaults;
@@ -170,7 +169,7 @@ fn execute(command: Execute, can_restart: bool) -> Result<PostExecutionAction, S
Cmd::Account(account_cmd) => account::execute(account_cmd).map(|s| PostExecutionAction::Print(s)),
Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd).map(|s| PostExecutionAction::Print(s)),
Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd).map(|_| PostExecutionAction::Quit),
Cmd::SignerToken(signer_cmd) => signer::execute(signer_cmd).map(|s| PostExecutionAction::Print(s)),
Cmd::SignerToken(ws_conf, ui_conf) => signer::execute(ws_conf, ui_conf).map(|s| PostExecutionAction::Print(s)),
Cmd::SignerSign { id, pwfile, port, authfile } => rpc_cli::signer_sign(id, pwfile, port, authfile).map(|s| PostExecutionAction::Print(s)),
Cmd::SignerList { port, authfile } => rpc_cli::signer_list(port, authfile).map(|s| PostExecutionAction::Print(s)),
Cmd::SignerReject { id, port, authfile } => rpc_cli::signer_reject(id, port, authfile).map(|s| PostExecutionAction::Print(s)),

View File

@@ -16,18 +16,24 @@
use std::io;
use std::sync::Arc;
use std::path::PathBuf;
use std::collections::HashSet;
use dapps;
use parity_rpc::informant::{RpcStats, Middleware};
use parity_rpc::{self as rpc, HttpServerError, Metadata, Origin, DomainsValidation};
use helpers::parity_ipc_path;
use dir::default_data_path;
use helpers::{parity_ipc_path, replace_home};
use jsonrpc_core::MetaIoHandler;
use parity_reactor::TokioRemote;
use parity_rpc::informant::{RpcStats, Middleware};
use parity_rpc::{self as rpc, Metadata, DomainsValidation};
use rpc_apis::{self, ApiSet};
pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware};
pub use parity_rpc::ws::Server as WsServer;
pub const DAPPS_DOMAIN: &'static str = "web3.site";
#[derive(Debug, Clone, PartialEq)]
pub struct HttpConfiguration {
pub enabled: bool,
@@ -39,6 +45,15 @@ pub struct HttpConfiguration {
pub threads: Option<usize>,
}
impl HttpConfiguration {
pub fn address(&self) -> Option<(String, u16)> {
match self.enabled {
true => Some((self.interface.clone(), self.port)),
false => None,
}
}
}
impl Default for HttpConfiguration {
fn default() -> Self {
HttpConfiguration {
@@ -53,6 +68,48 @@ impl Default for HttpConfiguration {
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct UiConfiguration {
pub enabled: bool,
pub interface: String,
pub port: u16,
pub hosts: Option<Vec<String>>,
}
impl UiConfiguration {
pub fn address(&self) -> Option<(String, u16)> {
match self.enabled {
true => Some((self.interface.clone(), self.port)),
false => None,
}
}
}
impl From<UiConfiguration> for HttpConfiguration {
fn from(conf: UiConfiguration) -> Self {
HttpConfiguration {
enabled: conf.enabled,
interface: conf.interface,
port: conf.port,
apis: rpc_apis::ApiSet::SafeContext,
cors: None,
hosts: conf.hosts,
threads: None,
}
}
}
impl Default for UiConfiguration {
fn default() -> Self {
UiConfiguration {
enabled: true,
port: 8180,
interface: "127.0.0.1".into(),
hosts: Some(vec![]),
}
}
}
#[derive(Debug, PartialEq)]
pub struct IpcConfiguration {
pub enabled: bool,
@@ -75,7 +132,7 @@ impl Default for IpcConfiguration {
}
}
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct WsConfiguration {
pub enabled: bool,
pub interface: String,
@@ -83,17 +140,32 @@ pub struct WsConfiguration {
pub apis: ApiSet,
pub origins: Option<Vec<String>>,
pub hosts: Option<Vec<String>>,
pub signer_path: PathBuf,
pub ui_address: Option<(String, u16)>,
}
impl Default for WsConfiguration {
fn default() -> Self {
let data_dir = default_data_path();
WsConfiguration {
enabled: true,
interface: "127.0.0.1".into(),
port: 8546,
apis: ApiSet::UnsafeContext,
origins: Some(Vec::new()),
origins: Some(vec!["chrome-extension://*".into()]),
hosts: Some(Vec::new()),
signer_path: replace_home(&data_dir, "$BASE/signer").into(),
ui_address: Some(("127.0.0.1".to_owned(), 8180)),
}
}
}
impl WsConfiguration {
pub fn address(&self) -> Option<(String, u16)> {
match self.enabled {
true => Some((self.interface.clone(), self.port)),
false => None,
}
}
}
@@ -104,62 +176,6 @@ pub struct Dependencies<D: rpc_apis::Dependencies> {
pub stats: Arc<RpcStats>,
}
pub struct RpcExtractor;
impl rpc::HttpMetaExtractor for RpcExtractor {
type Metadata = Metadata;
fn read_metadata(&self, origin: String, dapps_origin: Option<String>) -> Metadata {
let mut metadata = Metadata::default();
metadata.origin = match (origin.as_str(), dapps_origin) {
("null", Some(dapp)) => Origin::Dapps(dapp.into()),
_ => Origin::Rpc(origin),
};
metadata
}
}
impl rpc::IpcMetaExtractor<Metadata> for RpcExtractor {
fn extract(&self, _req: &rpc::IpcRequestContext) -> Metadata {
let mut metadata = Metadata::default();
// TODO [ToDr] Extract proper session id when it's available in context.
metadata.origin = Origin::Ipc(1.into());
metadata
}
}
struct WsRpcExtractor;
impl rpc::ws::MetaExtractor<Metadata> for WsRpcExtractor {
fn extract(&self, req: &rpc::ws::RequestContext) -> Metadata {
let mut metadata = Metadata::default();
let id = req.session_id as u64;
metadata.origin = Origin::Ws(id.into());
metadata.session = Some(Arc::new(rpc::PubSubSession::new(req.sender())));
metadata
}
}
struct WsStats {
stats: Arc<RpcStats>,
}
impl rpc::ws::SessionStats for WsStats {
fn open_session(&self, _id: rpc::ws::SessionId) {
self.stats.open_session()
}
fn close_session(&self, _id: rpc::ws::SessionId) {
self.stats.close_session()
}
}
fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
where D: rpc_apis::Dependencies
{
rpc_apis::setup_rpc(deps.stats.clone(), &*deps.apis, apis)
}
pub fn new_ws<D: rpc_apis::Dependencies>(
conf: WsConfiguration,
deps: &Dependencies<D>,
@@ -168,23 +184,41 @@ pub fn new_ws<D: rpc_apis::Dependencies>(
return Ok(None);
}
let url = format!("{}:{}", conf.interface, conf.port);
let domain = DAPPS_DOMAIN;
let ws_address = (conf.interface, conf.port);
let url = format!("{}:{}", ws_address.0, ws_address.1);
let addr = url.parse().map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?;
let handler = setup_apis(conf.apis, deps);
let remote = deps.remote.clone();
let allowed_origins = into_domains(conf.origins);
let allowed_hosts = into_domains(conf.hosts);
let full_handler = setup_apis(rpc_apis::ApiSet::SafeContext, deps);
let handler = {
let mut handler = MetaIoHandler::with_middleware((
rpc::WsDispatcher::new(full_handler),
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier())
));
let apis = conf.apis.list_apis().into_iter().collect::<Vec<_>>();
deps.apis.extend_with_set(&mut handler, &apis);
handler
};
let remote = deps.remote.clone();
let ui_address = conf.ui_address.clone();
let allowed_origins = into_domains(with_domain(conf.origins, domain, &[ui_address]));
let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &[Some(ws_address)]));
let signer_path = conf.signer_path;
let signer_path = conf.ui_address.map(move |_| ::signer::codes_path(&signer_path));
let path = signer_path.as_ref().map(|p| p.as_path());
let start_result = rpc::start_ws(
&addr,
handler,
remote.clone(),
allowed_origins,
allowed_hosts,
WsRpcExtractor,
WsStats {
stats: deps.stats.clone(),
},
rpc::WsExtractor::new(path.clone()),
rpc::WsExtractor::new(path.clone()),
rpc::WsStats::new(deps.stats.clone()),
);
match start_result {
@@ -197,21 +231,25 @@ pub fn new_ws<D: rpc_apis::Dependencies>(
}
pub fn new_http<D: rpc_apis::Dependencies>(
id: &str,
options: &str,
conf: HttpConfiguration,
deps: &Dependencies<D>,
middleware: Option<dapps::Middleware>
middleware: Option<dapps::Middleware>,
) -> Result<Option<HttpServer>, String> {
if !conf.enabled {
return Ok(None);
}
let url = format!("{}:{}", conf.interface, conf.port);
let addr = url.parse().map_err(|_| format!("Invalid HTTP JSON-RPC listen host/port given: {}", url))?;
let domain = DAPPS_DOMAIN;
let http_address = (conf.interface, conf.port);
let url = format!("{}:{}", http_address.0, http_address.1);
let addr = url.parse().map_err(|_| format!("Invalid {} listen host/port given: {}", id, url))?;
let handler = setup_apis(conf.apis, deps);
let remote = deps.remote.clone();
let cors_domains = into_domains(conf.cors);
let allowed_hosts = into_domains(conf.hosts);
let allowed_hosts = into_domains(with_domain(conf.hosts, domain, &[Some(http_address)]));
let start_result = rpc::start_http(
&addr,
@@ -219,7 +257,7 @@ pub fn new_http<D: rpc_apis::Dependencies>(
allowed_hosts,
handler,
remote,
RpcExtractor,
rpc::RpcExtractor,
match (conf.threads, middleware) {
(Some(threads), None) => rpc::HttpSettings::Threads(threads),
(None, middleware) => rpc::HttpSettings::Dapps(middleware),
@@ -231,17 +269,13 @@ pub fn new_http<D: rpc_apis::Dependencies>(
match start_result {
Ok(server) => Ok(Some(server)),
Err(HttpServerError::Io(ref err)) if err.kind() == io::ErrorKind::AddrInUse => Err(
format!("HTTP 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)
Err(rpc::HttpServerError::Io(ref err)) if err.kind() == io::ErrorKind::AddrInUse => Err(
format!("{} address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --{}-port and --{}-interface options.", id, url, options, options)
),
Err(e) => Err(format!("HTTP error: {:?}", e)),
Err(e) => Err(format!("{} error: {:?}", id, e)),
}
}
fn into_domains<T: From<String>>(items: Option<Vec<String>>) -> DomainsValidation<T> {
items.map(|vals| vals.into_iter().map(T::from).collect()).into()
}
pub fn new_ipc<D: rpc_apis::Dependencies>(
conf: IpcConfiguration,
dependencies: &Dependencies<D>
@@ -252,48 +286,39 @@ pub fn new_ipc<D: rpc_apis::Dependencies>(
let handler = setup_apis(conf.apis, dependencies);
let remote = dependencies.remote.clone();
let ipc = rpc::start_ipc(
&conf.socket_addr,
handler,
remote,
RpcExtractor,
);
match ipc {
match rpc::start_ipc(&conf.socket_addr, handler, remote, rpc::RpcExtractor) {
Ok(server) => Ok(Some(server)),
Err(io_error) => Err(format!("IPC error: {}", io_error)),
}
}
#[cfg(test)]
mod tests {
use super::RpcExtractor;
use parity_rpc::{HttpMetaExtractor, Origin};
#[test]
fn should_extract_rpc_origin() {
// given
let extractor = RpcExtractor;
// when
let meta = extractor.read_metadata("http://parity.io".into(), None);
let meta1 = extractor.read_metadata("http://parity.io".into(), Some("ignored".into()));
// then
assert_eq!(meta.origin, Origin::Rpc("http://parity.io".into()));
assert_eq!(meta1.origin, Origin::Rpc("http://parity.io".into()));
}
#[test]
fn should_dapps_origin() {
// given
let extractor = RpcExtractor;
let dapp = "https://wallet.ethereum.org".to_owned();
// when
let meta = extractor.read_metadata("null".into(), Some(dapp.clone()));
// then
assert_eq!(meta.origin, Origin::Dapps(dapp.into()));
}
fn into_domains<T: From<String>>(items: Option<Vec<String>>) -> DomainsValidation<T> {
items.map(|vals| vals.into_iter().map(T::from).collect()).into()
}
fn with_domain(items: Option<Vec<String>>, domain: &str, addresses: &[Option<(String, u16)>]) -> Option<Vec<String>> {
items.map(move |items| {
let mut items = items.into_iter().collect::<HashSet<_>>();
for address in addresses {
if let Some((host, port)) = address.clone() {
items.insert(format!("{}:{}", host, port));
items.insert(format!("{}:{}", host.replace("127.0.0.1", "localhost"), port));
items.insert(format!("http://*.{}:{}", domain, port));
items.insert(format!("http://*.{}", domain)); //proxypac
}
}
items.into_iter().collect()
})
}
fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
where D: rpc_apis::Dependencies
{
let mut handler = MetaIoHandler::with_middleware(
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier())
);
let apis = apis.list_apis().into_iter().collect::<Vec<_>>();
deps.apis.extend_with_set(&mut handler, &apis);
handler
}

View File

@@ -20,14 +20,15 @@ use std::collections::HashSet;
use std::str::FromStr;
use std::sync::{Arc, Weak};
pub use parity_rpc::SignerService;
pub use parity_rpc::signer::SignerService;
pub use parity_rpc::dapps::{DappsService, LocalDapp};
use ethcore::account_provider::AccountProvider;
use ethcore::client::Client;
use ethcore::miner::{Miner, ExternalMiner};
use ethcore::snapshot::SnapshotService;
use parity_rpc::{Metadata, NetworkSettings};
use parity_rpc::informant::{ActivityNotifier, Middleware, RpcStats, ClientNotifier};
use parity_rpc::informant::{ActivityNotifier, ClientNotifier};
use parity_rpc::dispatch::{FullDispatcher, LightDispatcher};
use ethsync::{ManageNetwork, SyncProvider, LightSync};
use hash_fetch::fetch::Client as FetchClient;
@@ -183,7 +184,11 @@ pub trait Dependencies {
fn activity_notifier(&self) -> Self::Notifier;
/// Extend the given I/O handler with endpoints for each API.
fn extend_with_set(&self, handler: &mut MetaIoHandler<Metadata, Middleware<Self::Notifier>>, apis: &[Api]);
fn extend_with_set<S>(
&self,
handler: &mut MetaIoHandler<Metadata, S>,
apis: &[Api],
) where S: core::Middleware<Metadata>;
}
/// RPC dependencies for a full node.
@@ -201,19 +206,20 @@ pub struct FullDependencies {
pub net_service: Arc<ManageNetwork>,
pub updater: Arc<Updater>,
pub geth_compatibility: bool,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>,
pub dapps_service: Option<Arc<DappsService>>,
pub dapps_address: Option<(String, u16)>,
pub ws_address: Option<(String, u16)>,
pub fetch: FetchClient,
pub remote: parity_reactor::Remote,
}
impl FullDependencies {
fn extend_api<T: core::Middleware<Metadata>>(
fn extend_api<S>(
&self,
handler: &mut MetaIoHandler<Metadata, T>,
handler: &mut MetaIoHandler<Metadata, S>,
apis: &[Api],
for_generic_pubsub: bool,
) {
) where S: core::Middleware<Metadata> {
use parity_rpc::v1::*;
macro_rules! add_signing_methods {
@@ -288,8 +294,8 @@ impl FullDependencies {
self.logger.clone(),
self.settings.clone(),
signer,
self.dapps_interface.clone(),
self.dapps_port,
self.dapps_address.clone(),
self.ws_address.clone(),
).to_delegate());
if !for_generic_pubsub {
@@ -312,6 +318,7 @@ impl FullDependencies {
&self.miner,
&self.updater,
&self.net_service,
self.dapps_service.clone(),
self.fetch.clone(),
).to_delegate())
},
@@ -339,7 +346,11 @@ impl Dependencies for FullDependencies {
}
}
fn extend_with_set(&self, handler: &mut MetaIoHandler<Metadata, Middleware<Self::Notifier>>, apis: &[Api]) {
fn extend_with_set<S>(
&self,
handler: &mut MetaIoHandler<Metadata, S>,
apis: &[Api],
) where S: core::Middleware<Metadata> {
self.extend_api(handler, apis, false)
}
}
@@ -363,8 +374,9 @@ pub struct LightDependencies {
pub on_demand: Arc<::light::on_demand::OnDemand>,
pub cache: Arc<Mutex<LightDataCache>>,
pub transaction_queue: Arc<RwLock<LightTransactionQueue>>,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>,
pub dapps_service: Option<Arc<DappsService>>,
pub dapps_address: Option<(String, u16)>,
pub ws_address: Option<(String, u16)>,
pub fetch: FetchClient,
pub geth_compatibility: bool,
pub remote: parity_reactor::Remote,
@@ -457,8 +469,8 @@ impl LightDependencies {
self.logger.clone(),
self.settings.clone(),
signer,
self.dapps_interface.clone(),
self.dapps_port,
self.dapps_address.clone(),
self.ws_address.clone(),
).to_delegate());
if !for_generic_pubsub {
@@ -479,6 +491,7 @@ impl LightDependencies {
Api::ParitySet => {
handler.extend_with(light::ParitySetClient::new(
self.sync.clone(),
self.dapps_service.clone(),
self.fetch.clone(),
).to_delegate())
},
@@ -502,7 +515,12 @@ impl Dependencies for LightDependencies {
type Notifier = LightClientNotifier;
fn activity_notifier(&self) -> Self::Notifier { LightClientNotifier }
fn extend_with_set(&self, handler: &mut MetaIoHandler<Metadata, Middleware<Self::Notifier>>, apis: &[Api]) {
fn extend_with_set<S>(
&self,
handler: &mut MetaIoHandler<Metadata, S>,
apis: &[Api],
) where S: core::Middleware<Metadata> {
self.extend_api(handler, apis, false)
}
}
@@ -552,15 +570,6 @@ impl ApiSet {
}
}
pub fn setup_rpc<D: Dependencies>(stats: Arc<RpcStats>, deps: &D, apis: ApiSet) -> MetaIoHandler<Metadata, Middleware<D::Notifier>> {
let mut handler = MetaIoHandler::with_middleware(Middleware::new(stats, deps.activity_notifier()));
// it's turned into vector, cause ont of the cases requires &[]
let apis = apis.list_apis().into_iter().collect::<Vec<_>>();
deps.extend_with_set(&mut handler, &apis[..]);
handler
}
#[cfg(test)]
mod test {
use super::{Api, ApiSet};

View File

@@ -49,11 +49,11 @@ use cache::CacheConfig;
use user_defaults::UserDefaults;
use dapps;
use ipfs;
use signer;
use secretstore;
use modules;
use rpc_apis;
use rpc;
use rpc_apis;
use secretstore;
use signer;
use url;
// how often to take periodic snapshots.
@@ -99,11 +99,10 @@ pub struct RunCmd {
pub wal: bool,
pub vm_type: VMType,
pub geth_compatibility: bool,
pub ui_address: Option<(String, u16)>,
pub net_settings: NetworkSettings,
pub dapps_conf: dapps::Configuration,
pub ipfs_conf: ipfs::Configuration,
pub signer_conf: signer::Configuration,
pub ui_conf: rpc::UiConfiguration,
pub secretstore_conf: secretstore::Configuration,
pub dapp: Option<String>,
pub ui: bool,
@@ -119,12 +118,12 @@ pub struct RunCmd {
pub no_persistent_txqueue: bool,
}
pub fn open_ui(signer_conf: &signer::Configuration) -> Result<(), String> {
if !signer_conf.enabled {
pub fn open_ui(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration) -> Result<(), String> {
if !ui_conf.enabled {
return Err("Cannot use UI command with UI turned off.".into())
}
let token = signer::generate_token_and_url(signer_conf)?;
let token = signer::generate_token_and_url(ws_conf, ui_conf)?;
// Open a browser
url::open(&token.url);
// Print a message
@@ -195,7 +194,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, compaction.clone())?;
// create dirs used by parity
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.signer_conf.enabled, cmd.secretstore_conf.enabled)?;
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.ui_conf.enabled, cmd.secretstore_conf.enabled)?;
info!("Starting {}", Colour::White.bold().paint(version()));
info!("Running in experimental {} mode.", Colour::Blue.bold().paint("Light Client"));
@@ -267,31 +266,47 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
// prepare account provider
let account_provider = Arc::new(prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?);
let rpc_stats = Arc::new(informant::RpcStats::default());
let signer_path = cmd.signer_conf.signer_path.clone();
// the dapps server
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.ui_conf));
let dapps_deps = {
let contract_client = Arc::new(::dapps::LightRegistrar {
client: service.client().clone(),
sync: light_sync.clone(),
on_demand: on_demand.clone(),
});
let sync = light_sync.clone();
dapps::Dependencies {
sync_status: Arc::new(move || sync.is_major_importing()),
contract_client: contract_client,
remote: event_loop.raw_remote(),
fetch: fetch.clone(),
signer: signer_service.clone(),
ui_address: cmd.ui_conf.address(),
}
};
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, dapps_deps)?;
// start RPCs
let dapps_service = dapps::service(&dapps_middleware);
let deps_for_rpc_apis = Arc::new(rpc_apis::LightDependencies {
signer_service: Arc::new(rpc_apis::SignerService::new(move || {
signer::generate_new_token(signer_path.clone()).map_err(|e| format!("{:?}", e))
}, cmd.ui_address)),
signer_service: signer_service,
client: service.client().clone(),
sync: light_sync.clone(),
net: light_sync.clone(),
secret_store: account_provider,
logger: logger,
settings: Arc::new(cmd.net_settings),
on_demand: on_demand.clone(),
on_demand: on_demand,
cache: cache,
transaction_queue: txq,
dapps_interface: match cmd.dapps_conf.enabled {
true => Some(cmd.http_conf.interface.clone()),
false => None,
},
dapps_port: match cmd.dapps_conf.enabled {
true => Some(cmd.http_conf.port),
false => None,
},
fetch: fetch.clone(),
dapps_service: dapps_service,
dapps_address: cmd.dapps_conf.address(cmd.http_conf.address()),
ws_address: cmd.ws_conf.address(),
fetch: fetch,
geth_compatibility: cmd.geth_compatibility,
remote: event_loop.remote(),
});
@@ -302,39 +317,11 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
stats: rpc_stats.clone(),
};
// the dapps server
let dapps_deps = {
let contract_client = Arc::new(::dapps::LightRegistrar {
client: service.client().clone(),
sync: light_sync.clone(),
on_demand: on_demand,
});
let sync = light_sync.clone();
dapps::Dependencies {
sync_status: Arc::new(move || sync.is_major_importing()),
contract_client: contract_client,
remote: event_loop.raw_remote(),
fetch: fetch,
signer: deps_for_rpc_apis.signer_service.clone(),
}
};
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps)?;
// start rpc servers
let _ws_server = rpc::new_ws(cmd.ws_conf, &dependencies)?;
let _http_server = rpc::new_http(cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
let _http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
let _ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
// the signer server
let signer_deps = signer::Dependencies {
apis: deps_for_rpc_apis.clone(),
remote: event_loop.raw_remote(),
rpc_stats: rpc_stats.clone(),
};
let signing_queue = deps_for_rpc_apis.signer_service.queue();
let _signer_server = signer::start(cmd.signer_conf.clone(), signing_queue, signer_deps)?;
let _ui_server = rpc::new_http("Parity Wallet (UI)", "ui", cmd.ui_conf.clone().into(), &dependencies, ui_middleware)?;
// minimal informant thread. Just prints block number every 5 seconds.
// TODO: integrate with informant.rs
@@ -351,9 +338,9 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> Result<(bool, Option<String>), String> {
if cmd.ui && cmd.dapps_conf.enabled {
// Check if Parity is already running
let addr = format!("{}:{}", cmd.signer_conf.interface, cmd.signer_conf.port);
let addr = format!("{}:{}", cmd.ui_conf.interface, cmd.ui_conf.port);
if !TcpListener::bind(&addr as &str).is_ok() {
return open_ui(&cmd.signer_conf).map(|_| (false, None));
return open_ui(&cmd.ws_conf, &cmd.ui_conf).map(|_| (false, None));
}
}
@@ -408,7 +395,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path()))?;
// create dirs used by parity
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.signer_conf.enabled, cmd.secretstore_conf.enabled)?;
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.ui_conf.enabled, cmd.secretstore_conf.enabled)?;
// run in daemon mode
if let Some(pid_file) = cmd.daemon {
@@ -620,16 +607,33 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
// set up dependencies for rpc servers
let rpc_stats = Arc::new(informant::RpcStats::default());
let signer_path = cmd.signer_conf.signer_path.clone();
let secret_store = match cmd.public_node {
true => None,
false => Some(account_provider.clone())
};
let signer_service = Arc::new(signer::new_service(&cmd.ws_conf, &cmd.ui_conf));
// the dapps server
let dapps_deps = {
let (sync, client) = (sync_provider.clone(), client.clone());
let contract_client = Arc::new(::dapps::FullRegistrar { client: client.clone() });
dapps::Dependencies {
sync_status: Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info())),
contract_client: contract_client,
remote: event_loop.raw_remote(),
fetch: fetch.clone(),
signer: signer_service.clone(),
ui_address: cmd.ui_conf.address(),
}
};
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, dapps_deps)?;
let dapps_service = dapps::service(&dapps_middleware);
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
signer_service: Arc::new(rpc_apis::SignerService::new(move || {
signer::generate_new_token(signer_path.clone()).map_err(|e| format!("{:?}", e))
}, cmd.ui_address)),
signer_service: signer_service,
snapshot: snapshot_service.clone(),
client: client.clone(),
sync: sync_provider.clone(),
@@ -642,14 +646,9 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
net_service: manage_network.clone(),
updater: updater.clone(),
geth_compatibility: cmd.geth_compatibility,
dapps_interface: match cmd.dapps_conf.enabled {
true => Some(cmd.http_conf.interface.clone()),
false => None,
},
dapps_port: match cmd.dapps_conf.enabled {
true => Some(cmd.http_conf.port),
false => None,
},
dapps_service: dapps_service,
dapps_address: cmd.dapps_conf.address(cmd.http_conf.address()),
ws_address: cmd.ws_conf.address(),
fetch: fetch.clone(),
remote: event_loop.remote(),
});
@@ -660,34 +659,12 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
stats: rpc_stats.clone(),
};
// the dapps server
let dapps_deps = {
let (sync, client) = (sync_provider.clone(), client.clone());
let contract_client = Arc::new(::dapps::FullRegistrar { client: client.clone() });
dapps::Dependencies {
sync_status: Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info())),
contract_client: contract_client,
remote: event_loop.raw_remote(),
fetch: fetch.clone(),
signer: deps_for_rpc_apis.signer_service.clone(),
}
};
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps)?;
// start rpc servers
let ws_server = rpc::new_ws(cmd.ws_conf, &dependencies)?;
let http_server = rpc::new_http(cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
let ws_server = rpc::new_ws(cmd.ws_conf.clone(), &dependencies)?;
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
// the signer server
let signer_deps = signer::Dependencies {
apis: deps_for_rpc_apis.clone(),
remote: event_loop.raw_remote(),
rpc_stats: rpc_stats.clone(),
};
let signing_queue = deps_for_rpc_apis.signer_service.queue();
let signer_server = signer::start(cmd.signer_conf.clone(), signing_queue, signer_deps)?;
let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
// the ui server
let ui_server = rpc::new_http("UI WALLET", "ui", cmd.ui_conf.clone().into(), &dependencies, ui_middleware)?;
// secret store key server
let secretstore_deps = secretstore::Dependencies {
@@ -746,7 +723,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
// start ui
if cmd.ui {
open_ui(&cmd.signer_conf)?;
open_ui(&cmd.ws_conf, &cmd.ui_conf)?;
}
if let Some(dapp) = cmd.dapp {
@@ -756,11 +733,11 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
// Handle exit
let restart = wait_for_exit(panic_handler, Some(updater), Some(client), can_restart);
// drop this stuff as soon as exit detected.
drop((ws_server, http_server, ipc_server, signer_server, secretstore_key_server, ipfs_server, event_loop));
info!("Finishing work, please wait...");
// drop this stuff as soon as exit detected.
drop((ws_server, http_server, ipc_server, ui_server, secretstore_key_server, ipfs_server, event_loop));
// to make sure timer does not spawn requests while shutdown is in progress
informant.shutdown();
// just Arc is dropping here, to allow other reference release in its default time

View File

@@ -15,51 +15,16 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::io;
use std::path::PathBuf;
use std::sync::Arc;
pub use ethcore_signer::Server as SignerServer;
use std::path::{Path, PathBuf};
use ansi_term::Colour;
use dir::default_data_path;
use parity_rpc::informant::RpcStats;
use parity_rpc::{self, ConfirmationsQueue};
use ethcore_signer as signer;
use helpers::replace_home;
use parity_reactor::TokioRemote;
use rpc;
use rpc_apis;
use parity_rpc;
use path::restrict_permissions_owner;
use util::H256;
const CODES_FILENAME: &'static str = "authcodes";
#[derive(Debug, PartialEq, Clone)]
pub struct Configuration {
pub enabled: bool,
pub port: u16,
pub interface: String,
pub signer_path: String,
pub skip_origin_validation: bool,
}
impl Default for Configuration {
fn default() -> Self {
let data_dir = default_data_path();
Configuration {
enabled: true,
port: 8180,
interface: "127.0.0.1".into(),
signer_path: replace_home(&data_dir, "$BASE/signer"),
skip_origin_validation: false,
}
}
}
pub struct Dependencies<D: rpc_apis::Dependencies> {
pub apis: Arc<D>,
pub remote: TokioRemote,
pub rpc_stats: Arc<RpcStats>,
}
pub const CODES_FILENAME: &'static str = "authcodes";
pub struct NewToken {
pub token: String,
@@ -67,42 +32,29 @@ pub struct NewToken {
pub message: String,
}
#[derive(Debug, Default, Clone)]
pub struct StandardExtractor;
impl signer::MetaExtractor<parity_rpc::Metadata> for StandardExtractor {
fn extract_metadata(&self, session: &H256) -> parity_rpc::Metadata {
let mut metadata = parity_rpc::Metadata::default();
metadata.origin = parity_rpc::Origin::Signer((*session).into());
metadata
}
pub fn new_service(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration) -> rpc_apis::SignerService {
let signer_path = ws_conf.signer_path.clone();
let signer_enabled = ui_conf.enabled;
rpc_apis::SignerService::new(move || {
generate_new_token(&signer_path).map_err(|e| format!("{:?}", e))
}, signer_enabled)
}
pub fn start<D: rpc_apis::Dependencies>(
conf: Configuration,
queue: Arc<ConfirmationsQueue>,
deps: Dependencies<D>,
) -> Result<Option<SignerServer>, String> {
if !conf.enabled {
Ok(None)
} else {
Ok(Some(do_start(conf, queue, deps)?))
}
}
fn codes_path(path: String) -> PathBuf {
let mut p = PathBuf::from(path);
pub fn codes_path(path: &Path) -> PathBuf {
let mut p = path.to_owned();
p.push(CODES_FILENAME);
let _ = restrict_permissions_owner(&p, true, false);
p
}
pub fn execute(cmd: Configuration) -> Result<String, String> {
Ok(generate_token_and_url(&cmd)?.message)
pub fn execute(ws_conf: rpc::WsConfiguration, ui_conf: rpc::UiConfiguration) -> Result<String, String> {
Ok(generate_token_and_url(&ws_conf, &ui_conf)?.message)
}
pub fn generate_token_and_url(conf: &Configuration) -> Result<NewToken, String> {
let code = generate_new_token(conf.signer_path.clone()).map_err(|err| format!("Error generating token: {}", err))?;
let auth_url = format!("http://{}:{}/#/auth?token={}", conf.interface, conf.port, code);
pub fn generate_token_and_url(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration) -> Result<NewToken, String> {
let code = generate_new_token(&ws_conf.signer_path).map_err(|err| format!("Error generating token: {:?}", err))?;
let auth_url = format!("http://{}:{}/#/auth?token={}", ui_conf.interface, ui_conf.port, code);
// And print in to the console
Ok(NewToken {
token: code.clone(),
@@ -119,49 +71,12 @@ Or use the generated token:
})
}
pub fn generate_new_token(path: String) -> io::Result<String> {
fn generate_new_token(path: &Path) -> io::Result<String> {
let path = codes_path(path);
let mut codes = signer::AuthCodes::from_file(&path)?;
let mut codes = parity_rpc::AuthCodes::from_file(&path)?;
codes.clear_garbage();
let code = codes.generate_new()?;
codes.to_file(&path)?;
trace!("New key code created: {}", Colour::White.bold().paint(&code[..]));
Ok(code)
}
fn do_start<D: rpc_apis::Dependencies>(
conf: Configuration,
queue: Arc<ConfirmationsQueue>,
deps: Dependencies<D>
) -> Result<SignerServer, String> {
let addr = format!("{}:{}", conf.interface, conf.port)
.parse()
.map_err(|_| format!("Invalid port specified: {}", conf.port))?;
let start_result = {
let server = signer::ServerBuilder::new(
queue,
codes_path(conf.signer_path),
);
if conf.skip_origin_validation {
warn!("{}", Colour::Red.bold().paint("*** INSECURE *** Running Trusted Signer with no origin validation."));
info!("If you do not intend this, exit now.");
}
let server = server.skip_origin_validation(conf.skip_origin_validation);
let server = server.stats(deps.rpc_stats.clone());
let handler = rpc_apis::setup_rpc(deps.rpc_stats, &*deps.apis, rpc_apis::ApiSet::SafeContext);
let remote = deps.remote.clone();
server.start_with_extractor(addr, handler, remote, StandardExtractor)
};
match start_result {
Err(signer::ServerError::IoError(err)) => match err.kind() {
io::ErrorKind::AddrInUse => Err(format!("Trusted UI address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ui-port and --ui-interface options.", addr)),
_ => Err(format!("Trusted Signer io error: {}", err)),
},
Err(e) => Err(format!("Trusted Signer Error: {:?}", e)),
Ok(server) => Ok(server),
}
}