From 88c9cea04d84272405d97b29649a3fe50aa06103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 9 Nov 2016 19:41:47 +0100 Subject: [PATCH] Dapps interface RPC (#3311) * Dapps Interface RPC * Adding JS apis * Support for signer interface in proxypac and embeds * Fixing tests * fixing tests again --- dapps/src/apps/fetcher.rs | 26 ++++++++++----------- dapps/src/apps/fs.rs | 4 ++-- dapps/src/apps/mod.rs | 12 +++++----- dapps/src/handlers/content.rs | 16 ++++++------- dapps/src/handlers/fetch.rs | 16 ++++++------- dapps/src/handlers/mod.rs | 8 +++---- dapps/src/lib.rs | 36 ++++++++++++++--------------- dapps/src/page/builtin.rs | 14 +++++------ dapps/src/page/handler.rs | 12 +++++----- dapps/src/page/local.rs | 12 +++++----- dapps/src/proxypac.rs | 12 +++++----- dapps/src/router/mod.rs | 16 ++++++------- dapps/src/tests/helpers.rs | 4 ++-- js/src/api/rpc/parity/parity.js | 5 ++++ js/src/jsonrpc/interfaces/parity.js | 9 ++++++++ parity/configuration.rs | 6 ++--- parity/dapps.rs | 10 ++++---- parity/rpc_apis.rs | 4 +++- parity/run.rs | 8 +++++-- rpc/src/v1/helpers/signer.rs | 18 +++++++-------- rpc/src/v1/impls/parity.rs | 13 ++++++++++- rpc/src/v1/tests/mocked/parity.rs | 25 ++++++++++++++++++-- rpc/src/v1/traits/parity.rs | 4 ++++ 23 files changed, 173 insertions(+), 117 deletions(-) diff --git a/dapps/src/apps/fetcher.rs b/dapps/src/apps/fetcher.rs index 041064121..2c1414201 100644 --- a/dapps/src/apps/fetcher.rs +++ b/dapps/src/apps/fetcher.rs @@ -47,7 +47,7 @@ pub struct ContentFetcher { resolver: R, cache: Arc>, sync: Arc, - embeddable_at: Option, + embeddable_on: Option<(String, u16)>, } impl Drop for ContentFetcher { @@ -59,7 +59,7 @@ impl Drop for ContentFetcher { impl ContentFetcher { - pub fn new(resolver: R, sync_status: Arc, embeddable_at: Option) -> Self { + pub fn new(resolver: R, sync_status: Arc, embeddable_on: Option<(String, u16)>) -> Self { let mut dapps_path = env::temp_dir(); dapps_path.push(random_filename()); @@ -68,17 +68,17 @@ impl ContentFetcher { resolver: resolver, sync: sync_status, cache: Arc::new(Mutex::new(ContentCache::default())), - embeddable_at: embeddable_at, + embeddable_on: embeddable_on, } } - fn still_syncing(port: Option) -> Box { + fn still_syncing(address: Option<(String, u16)>) -> Box { Box::new(ContentHandler::error( StatusCode::ServiceUnavailable, "Sync In Progress", "Your node is still syncing. We cannot resolve any content before it's fully synced.", Some("Refresh"), - port, + address, )) } @@ -145,7 +145,7 @@ impl ContentFetcher { match content { // Don't serve dapps if we are still syncing (but serve content) Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => { - (None, Self::still_syncing(self.embeddable_at)) + (None, Self::still_syncing(self.embeddable_on.clone())) }, Some(URLHintResult::Dapp(dapp)) => { let (handler, fetch_control) = ContentFetcherHandler::new( @@ -155,9 +155,9 @@ impl ContentFetcher { id: content_id.clone(), dapps_path: self.dapps_path.clone(), on_done: Box::new(on_done), - embeddable_at: self.embeddable_at, + embeddable_on: self.embeddable_on.clone(), }, - self.embeddable_at, + self.embeddable_on.clone(), ); (Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box) @@ -172,13 +172,13 @@ impl ContentFetcher { content_path: self.dapps_path.clone(), on_done: Box::new(on_done), }, - self.embeddable_at, + self.embeddable_on.clone(), ); (Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box) }, None if self.sync.is_major_importing() => { - (None, Self::still_syncing(self.embeddable_at)) + (None, Self::still_syncing(self.embeddable_on.clone())) }, None => { // This may happen when sync status changes in between @@ -188,7 +188,7 @@ impl ContentFetcher { "Resource Not Found", "Requested resource was not found.", None, - self.embeddable_at, + self.embeddable_on.clone(), )) as Box) }, } @@ -293,7 +293,7 @@ struct DappInstaller { id: String, dapps_path: PathBuf, on_done: Box) + Send>, - embeddable_at: Option, + embeddable_on: Option<(String, u16)>, } impl DappInstaller { @@ -386,7 +386,7 @@ impl ContentValidator for DappInstaller { try!(manifest_file.write_all(manifest_str.as_bytes())); // Create endpoint - let app = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_at); + let app = LocalPageEndpoint::new(target, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone()); // Return modified app manifest Ok((manifest.id.clone(), app)) diff --git a/dapps/src/apps/fs.rs b/dapps/src/apps/fs.rs index 104b33035..f0b4ddfa8 100644 --- a/dapps/src/apps/fs.rs +++ b/dapps/src/apps/fs.rs @@ -97,12 +97,12 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo { }) } -pub fn local_endpoints(dapps_path: String, signer_port: Option) -> Endpoints { +pub fn local_endpoints(dapps_path: String, signer_address: Option<(String, u16)>) -> Endpoints { let mut pages = Endpoints::new(); for dapp in local_dapps(dapps_path) { pages.insert( dapp.id, - Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_port)) + Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone())) ); } pages diff --git a/dapps/src/apps/mod.rs b/dapps/src/apps/mod.rs index 11919d6d2..3cb0d8256 100644 --- a/dapps/src/apps/mod.rs +++ b/dapps/src/apps/mod.rs @@ -37,26 +37,26 @@ pub fn utils() -> Box { Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned())) } -pub fn all_endpoints(dapps_path: String, signer_port: Option) -> Endpoints { +pub fn all_endpoints(dapps_path: String, signer_address: Option<(String, u16)>) -> Endpoints { // fetch fs dapps at first to avoid overwriting builtins - let mut pages = fs::local_endpoints(dapps_path, signer_port); + let mut pages = fs::local_endpoints(dapps_path, signer_address.clone()); // NOTE [ToDr] Dapps will be currently embeded on 8180 - insert::(&mut pages, "ui", Embeddable::Yes(signer_port)); - pages.insert("proxy".into(), ProxyPac::boxed(signer_port)); + insert::(&mut pages, "ui", Embeddable::Yes(signer_address.clone())); + pages.insert("proxy".into(), ProxyPac::boxed(signer_address)); pages } fn insert(pages: &mut Endpoints, id: &str, embed_at: Embeddable) { pages.insert(id.to_owned(), Box::new(match embed_at { - Embeddable::Yes(port) => PageEndpoint::new_safe_to_embed(T::default(), port), + Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address), Embeddable::No => PageEndpoint::new(T::default()), })); } enum Embeddable { - Yes(Option), + Yes(Option<(String, u16)>), #[allow(dead_code)] No, } diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs index a67fbcd0b..738a9a890 100644 --- a/dapps/src/handlers/content.rs +++ b/dapps/src/handlers/content.rs @@ -32,7 +32,7 @@ pub struct ContentHandler { content: String, mimetype: Mime, write_pos: usize, - safe_to_embed_at_port: Option, + safe_to_embed_on: Option<(String, u16)>, } impl ContentHandler { @@ -44,31 +44,31 @@ impl ContentHandler { Self::new(StatusCode::NotFound, content, mimetype) } - pub fn html(code: StatusCode, content: String, embeddable_at: Option) -> Self { - Self::new_embeddable(code, content, mime!(Text/Html), embeddable_at) + pub fn html(code: StatusCode, content: String, embeddable_on: Option<(String, u16)>) -> Self { + Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on) } - pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_at: Option) -> Self { + pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_on: Option<(String, u16)>) -> Self { Self::html(code, format!( include_str!("../error_tpl.html"), title=title, message=message, details=details.unwrap_or_else(|| ""), version=version(), - ), embeddable_at) + ), embeddable_on) } pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self { Self::new_embeddable(code, content, mimetype, None) } - pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_at: Option) -> Self { + pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self { ContentHandler { code: code, content: content, mimetype: mimetype, write_pos: 0, - safe_to_embed_at_port: embeddable_at, + safe_to_embed_on: embeddable_on, } } } @@ -85,7 +85,7 @@ impl server::Handler for ContentHandler { fn on_response(&mut self, res: &mut server::Response) -> Next { res.set_status(self.code); res.headers_mut().set(header::ContentType(self.mimetype.clone())); - add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port.clone()); + add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone()); Next::write() } diff --git a/dapps/src/handlers/fetch.rs b/dapps/src/handlers/fetch.rs index 1a0221bff..a34b58fa7 100644 --- a/dapps/src/handlers/fetch.rs +++ b/dapps/src/handlers/fetch.rs @@ -138,7 +138,7 @@ pub struct ContentFetcherHandler { client: Option, installer: H, request_url: Option, - embeddable_at: Option, + embeddable_on: Option<(String, u16)>, } impl Drop for ContentFetcherHandler { @@ -157,7 +157,7 @@ impl ContentFetcherHandler { url: String, control: Control, handler: H, - embeddable_at: Option, + embeddable_on: Option<(String, u16)>, ) -> (Self, Arc) { let fetch_control = Arc::new(FetchControl::default()); @@ -169,7 +169,7 @@ impl ContentFetcherHandler { status: FetchState::NotStarted(url), installer: handler, request_url: None, - embeddable_at: embeddable_at, + embeddable_on: embeddable_on, }; (handler, fetch_control) @@ -208,7 +208,7 @@ impl server::Handler for ContentFetcherHandler< "Unable To Start Dapp Download", "Could not initialize download of the dapp. It might be a problem with the remote server.", Some(&format!("{}", e)), - self.embeddable_at, + self.embeddable_on.clone(), )), } }, @@ -218,7 +218,7 @@ impl server::Handler for ContentFetcherHandler< "Method Not Allowed", "Only GET requests are allowed.", None, - self.embeddable_at, + self.embeddable_on.clone(), )), }) } else { None }; @@ -241,7 +241,7 @@ impl server::Handler for ContentFetcherHandler< "Download Timeout", &format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT), None, - self.embeddable_at, + self.embeddable_on.clone(), ); Self::close_client(&mut self.client); (Some(FetchState::Error(timeout)), Next::write()) @@ -263,7 +263,7 @@ impl server::Handler for ContentFetcherHandler< "Invalid Dapp", "Downloaded bundle does not contain a valid content.", Some(&format!("{:?}", e)), - self.embeddable_at, + self.embeddable_on.clone(), )) }, Ok((id, result)) => { @@ -284,7 +284,7 @@ impl server::Handler for ContentFetcherHandler< "Download Error", "There was an error when fetching the content.", Some(&format!("{:?}", e)), - self.embeddable_at, + self.embeddable_on.clone(), ); (Some(FetchState::Error(error)), Next::write()) }, diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs index 3d96e8a40..b575509a5 100644 --- a/dapps/src/handlers/mod.rs +++ b/dapps/src/handlers/mod.rs @@ -30,18 +30,18 @@ pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl}; use url::Url; use hyper::{server, header, net, uri}; -use signer_address; +use address; /// Adds security-related headers to the Response. -pub fn add_security_headers(headers: &mut header::Headers, embeddable_at: Option) { +pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option<(String, u16)>) { headers.set_raw("X-XSS-Protection", vec![b"1; mode=block".to_vec()]); headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]); // Embedding header: - if let Some(port) = embeddable_at { + if let Some(embeddable_on) = embeddable_on { headers.set_raw( "X-Frame-Options", - vec![format!("ALLOW-FROM http://{}", signer_address(port)).into_bytes()] + vec![format!("ALLOW-FROM http://{}", address(embeddable_on)).into_bytes()] ); } else { // TODO [ToDr] Should we be more strict here (DENY?)? diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 4394d1183..2c9fa33d1 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -112,7 +112,7 @@ pub struct ServerBuilder { handler: Arc, registrar: Arc, sync_status: Arc, - signer_port: Option, + signer_address: Option<(String, u16)>, } impl Extendable for ServerBuilder { @@ -129,7 +129,7 @@ impl ServerBuilder { handler: Arc::new(IoHandler::new()), registrar: registrar, sync_status: Arc::new(|| false), - signer_port: None, + signer_address: None, } } @@ -139,8 +139,8 @@ impl ServerBuilder { } /// Change default signer port. - pub fn with_signer_port(&mut self, signer_port: Option) { - self.signer_port = signer_port; + pub fn with_signer_address(&mut self, signer_address: Option<(String, u16)>) { + self.signer_address = signer_address; } /// Asynchronously start server with no authentication, @@ -152,7 +152,7 @@ impl ServerBuilder { NoAuth, self.handler.clone(), self.dapps_path.clone(), - self.signer_port.clone(), + self.signer_address.clone(), self.registrar.clone(), self.sync_status.clone(), ) @@ -167,7 +167,7 @@ impl ServerBuilder { HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone(), - self.signer_port.clone(), + self.signer_address.clone(), self.registrar.clone(), self.sync_status.clone(), ) @@ -197,11 +197,11 @@ impl Server { } /// Returns a list of CORS domains for API endpoint. - fn cors_domains(signer_port: Option) -> Vec { - match signer_port { - Some(port) => vec![ + fn cors_domains(signer_address: Option<(String, u16)>) -> Vec { + match signer_address { + Some(signer_address) => vec![ format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN), - format!("http://{}", signer_address(port)), + format!("http://{}", address(signer_address)), ], None => vec![], } @@ -213,15 +213,15 @@ impl Server { authorization: A, handler: Arc, dapps_path: String, - signer_port: Option, + signer_address: Option<(String, u16)>, registrar: Arc, sync_status: Arc, ) -> Result { let panic_handler = Arc::new(Mutex::new(None)); let authorization = Arc::new(authorization); - let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status, signer_port)); - let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_port.clone())); - let cors_domains = Self::cors_domains(signer_port); + let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(apps::urlhint::URLHintContract::new(registrar), sync_status, signer_address.clone())); + let endpoints = Arc::new(apps::all_endpoints(dapps_path, signer_address.clone())); + let cors_domains = Self::cors_domains(signer_address.clone()); let special = Arc::new({ let mut special = HashMap::new(); @@ -238,7 +238,7 @@ impl Server { try!(hyper::Server::http(addr)) .handle(move |ctrl| router::Router::new( ctrl, - signer_port.clone(), + signer_address.clone(), content_fetcher.clone(), endpoints.clone(), special.clone(), @@ -302,8 +302,8 @@ pub fn random_filename() -> String { rng.gen_ascii_chars().take(12).collect() } -fn signer_address(port: u16) -> String { - format!("127.0.0.1:{}", port) +fn address(address: (String, u16)) -> String { + format!("{}:{}", address.0, address.1) } #[cfg(test)] @@ -332,7 +332,7 @@ mod util_tests { // when let none = Server::cors_domains(None); - let some = Server::cors_domains(Some(18180)); + let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180))); // then assert_eq!(none, Vec::::new()); diff --git a/dapps/src/page/builtin.rs b/dapps/src/page/builtin.rs index bf5bf088f..40c0e23a6 100644 --- a/dapps/src/page/builtin.rs +++ b/dapps/src/page/builtin.rs @@ -25,7 +25,7 @@ pub struct PageEndpoint { /// Prefix to strip from the path (when `None` deducted from `app_id`) pub prefix: Option, /// Safe to be loaded in frame by other origin. (use wisely!) - safe_to_embed_at_port: Option, + safe_to_embed_on: Option<(String, u16)>, info: EndpointInfo, } @@ -36,7 +36,7 @@ impl PageEndpoint { PageEndpoint { app: Arc::new(app), prefix: None, - safe_to_embed_at_port: None, + safe_to_embed_on: None, info: EndpointInfo::from(info), } } @@ -49,7 +49,7 @@ impl PageEndpoint { PageEndpoint { app: Arc::new(app), prefix: Some(prefix), - safe_to_embed_at_port: None, + safe_to_embed_on: None, info: EndpointInfo::from(info), } } @@ -57,12 +57,12 @@ impl PageEndpoint { /// Creates new `PageEndpoint` which can be safely used in iframe /// even from different origin. It might be dangerous (clickjacking). /// Use wisely! - pub fn new_safe_to_embed(app: T, port: Option) -> Self { + pub fn new_safe_to_embed(app: T, address: Option<(String, u16)>) -> Self { let info = app.info(); PageEndpoint { app: Arc::new(app), prefix: None, - safe_to_embed_at_port: port, + safe_to_embed_on: address, info: EndpointInfo::from(info), } } @@ -79,9 +79,9 @@ impl Endpoint for PageEndpoint { app: BuiltinDapp::new(self.app.clone()), prefix: self.prefix.clone(), path: path, - file: handler::ServedFile::new(self.safe_to_embed_at_port.clone()), + file: handler::ServedFile::new(self.safe_to_embed_on.clone()), cache: PageCache::Disabled, - safe_to_embed_at_port: self.safe_to_embed_at_port.clone(), + safe_to_embed_on: self.safe_to_embed_on.clone(), }) } } diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index f908a69c5..74eabf917 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -60,13 +60,13 @@ pub enum ServedFile { } impl ServedFile { - pub fn new(embeddable_at: Option) -> Self { + pub fn new(embeddable_on: Option<(String, u16)>) -> Self { ServedFile::Error(ContentHandler::error( StatusCode::NotFound, "404 Not Found", "Requested dapp resource was not found.", None, - embeddable_at, + embeddable_on, )) } } @@ -97,7 +97,7 @@ pub struct PageHandler { /// Requested path. pub path: EndpointPath, /// Flag indicating if the file can be safely embeded (put in iframe). - pub safe_to_embed_at_port: Option, + pub safe_to_embed_on: Option<(String, u16)>, /// Cache settings for this page. pub cache: PageCache, } @@ -133,7 +133,7 @@ impl server::Handler for PageHandler { self.app.file(&self.extract_path(url.path())) }, _ => None, - }.map_or_else(|| ServedFile::new(self.safe_to_embed_at_port.clone()), |f| ServedFile::File(f)); + }.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f)); Next::write() } @@ -162,7 +162,7 @@ impl server::Handler for PageHandler { } // Security headers: - add_security_headers(&mut res.headers_mut(), self.safe_to_embed_at_port); + add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone()); Next::write() }, ServedFile::Error(ref mut handler) => { @@ -246,7 +246,7 @@ fn should_extract_path_with_appid() { }, file: ServedFile::new(None), cache: Default::default(), - safe_to_embed_at_port: None, + safe_to_embed_on: None, }; // when diff --git a/dapps/src/page/local.rs b/dapps/src/page/local.rs index aa98a68cd..ec24cac36 100644 --- a/dapps/src/page/local.rs +++ b/dapps/src/page/local.rs @@ -27,17 +27,17 @@ pub struct LocalPageEndpoint { mime: Option, info: Option, cache: PageCache, - embeddable_at: Option, + embeddable_on: Option<(String, u16)>, } impl LocalPageEndpoint { - pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_at: Option) -> Self { + pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Option<(String, u16)>) -> Self { LocalPageEndpoint { path: path, mime: None, info: Some(info), cache: cache, - embeddable_at: embeddable_at, + embeddable_on: embeddable_on, } } @@ -47,7 +47,7 @@ impl LocalPageEndpoint { mime: Some(mime), info: None, cache: cache, - embeddable_at: None, + embeddable_on: None, } } @@ -68,7 +68,7 @@ impl Endpoint for LocalPageEndpoint { prefix: None, path: path, file: handler::ServedFile::new(None), - safe_to_embed_at_port: self.embeddable_at, + safe_to_embed_on: self.embeddable_on.clone(), cache: self.cache, }) } else { @@ -77,7 +77,7 @@ impl Endpoint for LocalPageEndpoint { prefix: None, path: path, file: handler::ServedFile::new(None), - safe_to_embed_at_port: self.embeddable_at, + safe_to_embed_on: self.embeddable_on.clone(), cache: self.cache, }) } diff --git a/dapps/src/proxypac.rs b/dapps/src/proxypac.rs index 2d7c4e3ce..88ecb6ab3 100644 --- a/dapps/src/proxypac.rs +++ b/dapps/src/proxypac.rs @@ -19,24 +19,24 @@ use endpoint::{Endpoint, Handler, EndpointPath}; use handlers::ContentHandler; use apps::{HOME_PAGE, DAPPS_DOMAIN}; -use signer_address; +use address; pub struct ProxyPac { - signer_port: Option, + signer_address: Option<(String, u16)>, } impl ProxyPac { - pub fn boxed(signer_port: Option) -> Box { + pub fn boxed(signer_address: Option<(String, u16)>) -> Box { Box::new(ProxyPac { - signer_port: signer_port + signer_address: signer_address }) } } impl Endpoint for ProxyPac { fn to_handler(&self, path: EndpointPath) -> Box { - let signer = self.signer_port - .map(signer_address) + let signer = self.signer_address.clone() + .map(address) .unwrap_or_else(|| format!("{}:{}", path.host, path.port)); let content = format!( diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index fe8061ef0..cb9133886 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -20,7 +20,7 @@ pub mod auth; mod host_validation; -use signer_address; +use address; use std::sync::Arc; use std::collections::HashMap; use url::{Url, Host}; @@ -43,7 +43,7 @@ pub enum SpecialEndpoint { pub struct Router { control: Option, - signer_port: Option, + signer_address: Option<(String, u16)>, endpoints: Arc, fetch: Arc, special: Arc>>, @@ -117,14 +117,14 @@ impl server::Handler for Router { "404 Not Found", "Requested content was not found.", None, - self.signer_port, + self.signer_address.clone(), )) }, // Redirect any other GET request to signer. _ if *req.method() == hyper::Method::Get => { - if let Some(port) = self.signer_port { + if let Some(signer_address) = self.signer_address.clone() { trace!(target: "dapps", "Redirecting to signer interface."); - Redirection::boxed(&format!("http://{}", signer_address(port))) + Redirection::boxed(&format!("http://{}", address(signer_address))) } else { trace!(target: "dapps", "Signer disabled, returning 404."); Box::new(ContentHandler::error( @@ -132,7 +132,7 @@ impl server::Handler for Router { "404 Not Found", "Your homepage is not available when Trusted Signer is disabled.", Some("You can still access dapps by writing a correct address, though. Re-enabled Signer to get your homepage back."), - self.signer_port, + self.signer_address.clone(), )) } }, @@ -168,7 +168,7 @@ impl server::Handler for Router { impl Router { pub fn new( control: Control, - signer_port: Option, + signer_address: Option<(String, u16)>, content_fetcher: Arc, endpoints: Arc, special: Arc>>, @@ -181,7 +181,7 @@ impl Router { .to_handler(EndpointPath::default()); Router { control: Some(control), - signer_port: signer_port, + signer_address: signer_address, endpoints: endpoints, fetch: content_fetcher, special: special, diff --git a/dapps/src/tests/helpers.rs b/dapps/src/tests/helpers.rs index 1fa2e777a..f7c9e8ba6 100644 --- a/dapps/src/tests/helpers.rs +++ b/dapps/src/tests/helpers.rs @@ -76,7 +76,7 @@ pub fn init_server(hosts: Option>, is_syncing: bool) -> (Server, Arc dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar.clone()); builder.with_sync_status(Arc::new(move || is_syncing)); - builder.with_signer_port(Some(SIGNER_PORT)); + builder.with_signer_address(Some(("127.0.0.1".into(), SIGNER_PORT))); ( builder.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), hosts).unwrap(), registrar, @@ -89,7 +89,7 @@ pub fn serve_with_auth(user: &str, pass: &str) -> Server { let mut dapps_path = env::temp_dir(); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); let mut builder = ServerBuilder::new(dapps_path.to_str().unwrap().into(), registrar); - builder.with_signer_port(Some(SIGNER_PORT)); + builder.with_signer_address(Some(("127.0.0.1".into(), SIGNER_PORT))); builder.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), None, user, pass).unwrap() } diff --git a/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js index f1739f848..5999c9d67 100644 --- a/js/src/api/rpc/parity/parity.js +++ b/js/src/api/rpc/parity/parity.js @@ -60,6 +60,11 @@ export default class Parity { .then(outNumber); } + dappsInterface () { + return this._transport + .execute('parity_dappsInterface'); + } + defaultExtraData () { return this._transport .execute('parity_defaultExtraData'); diff --git a/js/src/jsonrpc/interfaces/parity.js b/js/src/jsonrpc/interfaces/parity.js index 66a8ea962..883ad9675 100644 --- a/js/src/jsonrpc/interfaces/parity.js +++ b/js/src/jsonrpc/interfaces/parity.js @@ -109,6 +109,15 @@ export default { } }, + dappsInterface: { + desc: 'Returns the interface the dapps are running on, error if not enabled', + params: [], + returns: { + type: String, + desc: 'The interface' + } + }, + defaultExtraData: { desc: 'Returns the default extra data', params: [], diff --git a/parity/configuration.rs b/parity/configuration.rs index 1d46dda18..75d319272 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -95,7 +95,7 @@ impl Configuration { let wal = !self.args.flag_fast_and_loose; let warp_sync = self.args.flag_warp; let geth_compatibility = self.args.flag_geth; - let ui_port = self.ui_port(); + let ui_address = self.ui_port().map(|port| (self.ui_interface(), port)); let dapps_conf = self.dapps_config(); let signer_conf = self.signer_config(); let format = try!(self.format()); @@ -243,7 +243,7 @@ impl Configuration { vm_type: vm_type, warp_sync: warp_sync, geth_compatibility: geth_compatibility, - ui_port: ui_port, + ui_address: ui_address, net_settings: self.network_settings(), dapps_conf: dapps_conf, signer_conf: signer_conf, @@ -859,7 +859,7 @@ mod tests { wal: true, vm_type: Default::default(), geth_compatibility: false, - ui_port: Some(8180), + ui_address: Some(("127.0.0.1".into(), 8180)), net_settings: Default::default(), dapps_conf: Default::default(), signer_conf: Default::default(), diff --git a/parity/dapps.rs b/parity/dapps.rs index 6ef64c2fd..80f2f7060 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -58,7 +58,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result Result>, _auth: Option<(String, String)>, - _signer_port: Option, + _signer_address: Option<(String, u16)>, ) -> Result { Err("Your Parity version has been compiled without WebApps support.".into()) } @@ -120,7 +120,7 @@ mod server { url: &SocketAddr, allowed_hosts: Option>, auth: Option<(String, String)>, - signer_port: Option, + signer_address: Option<(String, u16)>, ) -> Result { use ethcore_dapps as dapps; @@ -131,7 +131,7 @@ mod server { let sync = deps.sync.clone(); let client = deps.client.clone(); server.with_sync_status(Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info()))); - server.with_signer_port(signer_port); + server.with_signer_address(signer_address); let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext); let start_result = match auth { diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 9ffd8e0dd..2d375fdde 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -37,7 +37,7 @@ pub enum Api { Net, /// Eth (Safe) Eth, - /// Geth-compatible "personal" API (DEPRECATED; only used in `--geth` mode.) + /// Geth-compatible "personal" API (DEPRECATED; only used in `--geth` mode.) Personal, /// Signer - Confirm transactions in Signer (UNSAFE: Passwords, List of transactions) Signer, @@ -119,6 +119,7 @@ pub struct Dependencies { pub settings: Arc, pub net_service: Arc, pub geth_compatibility: bool, + pub dapps_interface: Option, pub dapps_port: Option, } @@ -228,6 +229,7 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet deps.logger.clone(), deps.settings.clone(), signer, + deps.dapps_interface.clone(), deps.dapps_port, ).to_delegate()); diff --git a/parity/run.rs b/parity/run.rs index 2af0d35ca..56ff92c25 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -82,7 +82,7 @@ pub struct RunCmd { pub wal: bool, pub vm_type: VMType, pub geth_compatibility: bool, - pub ui_port: Option, + pub ui_address: Option<(String, u16)>, pub net_settings: NetworkSettings, pub dapps_conf: dapps::Configuration, pub signer_conf: signer::Configuration, @@ -262,7 +262,7 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies { signer_service: Arc::new(rpc_apis::SignerService::new(move || { signer::generate_new_token(signer_path.clone()).map_err(|e| format!("{:?}", e)) - }, cmd.ui_port)), + }, cmd.ui_address)), snapshot: snapshot_service.clone(), client: client.clone(), sync: sync_provider.clone(), @@ -274,6 +274,10 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { settings: Arc::new(cmd.net_settings.clone()), net_service: manage_network.clone(), geth_compatibility: cmd.geth_compatibility, + dapps_interface: match cmd.dapps_conf.enabled { + true => Some(cmd.dapps_conf.interface.clone()), + false => None, + }, dapps_port: match cmd.dapps_conf.enabled { true => Some(cmd.dapps_conf.port), false => None, diff --git a/rpc/src/v1/helpers/signer.rs b/rpc/src/v1/helpers/signer.rs index d4a5af273..11f8e3376 100644 --- a/rpc/src/v1/helpers/signer.rs +++ b/rpc/src/v1/helpers/signer.rs @@ -22,18 +22,18 @@ use v1::helpers::signing_queue::{ConfirmationsQueue}; pub struct SignerService { queue: Arc, generate_new_token: Box Result + Send + Sync + 'static>, - port: Option, + address: Option<(String, u16)>, } impl SignerService { /// Creates new Signer Service given function to generate new tokens. - pub fn new(new_token: F, port: Option) -> Self + pub fn new(new_token: F, address: Option<(String, u16)>) -> Self where F: Fn() -> Result + Send + Sync + 'static { SignerService { queue: Arc::new(ConfirmationsQueue::default()), generate_new_token: Box::new(new_token), - port: port, + address: address, } } @@ -47,20 +47,20 @@ impl SignerService { self.queue.clone() } - /// Returns signer port (if signer enabled) or `None` otherwise - pub fn port(&self) -> Option { - self.port + /// Returns signer address (if signer enabled) or `None` otherwise + pub fn address(&self) -> Option<(String, u16)> { + self.address.clone() } /// Returns true if Signer is enabled. pub fn is_enabled(&self) -> bool { - self.port.is_some() + self.address.is_some() } #[cfg(test)] /// Creates new Signer Service for tests. - pub fn new_test(port: Option) -> Self { - SignerService::new(|| Ok("new_token".into()), port) + pub fn new_test(address: Option<(String, u16)>) -> Self { + SignerService::new(|| Ok("new_token".into()), address) } } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index b9c19f667..1b8ee9695 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -52,6 +52,7 @@ pub struct ParityClient where logger: Arc, settings: Arc, signer: Option>, + dapps_interface: Option, dapps_port: Option, } @@ -70,6 +71,7 @@ impl ParityClient where logger: Arc, settings: Arc, signer: Option>, + dapps_interface: Option, dapps_port: Option, ) -> Self { ParityClient { @@ -81,6 +83,7 @@ impl ParityClient where logger: logger, settings: settings, signer: signer, + dapps_interface: dapps_interface, dapps_port: dapps_port, } } @@ -261,7 +264,8 @@ impl Parity for ParityClient where self.signer .clone() - .and_then(|signer| signer.port()) + .and_then(|signer| signer.address()) + .map(|address| address.1) .ok_or_else(|| errors::signer_disabled()) } @@ -272,6 +276,13 @@ impl Parity for ParityClient where .ok_or_else(|| errors::dapps_disabled()) } + fn dapps_interface(&self) -> Result { + try!(self.active()); + + self.dapps_interface.clone() + .ok_or_else(|| errors::dapps_disabled()) + } + fn next_nonce(&self, address: H160) -> Result { try!(self.active()); let address: Address = address.into(); diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 8baecbd90..b5c8187c7 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -38,6 +38,7 @@ pub struct Dependencies { pub settings: Arc, pub network: Arc, pub accounts: Arc, + pub dapps_interface: Option, pub dapps_port: Option, } @@ -61,6 +62,7 @@ impl Dependencies { }), network: Arc::new(TestManageNetwork), accounts: Arc::new(AccountProvider::transient_provider()), + dapps_interface: Some("127.0.0.1".into()), dapps_port: Some(18080), } } @@ -75,6 +77,7 @@ impl Dependencies { self.logger.clone(), self.settings.clone(), signer, + self.dapps_interface.clone(), self.dapps_port, ) } @@ -238,7 +241,7 @@ fn rpc_parity_node_name() { #[test] fn rpc_parity_unsigned_transactions_count() { let deps = Dependencies::new(); - let io = deps.with_signer(SignerService::new_test(Some(18180))); + let io = deps.with_signer(SignerService::new_test(Some(("127.0.0.1".into(), 18180)))); let request = r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":0,"id":1}"#; @@ -282,7 +285,7 @@ fn rpc_parity_encrypt() { fn rpc_parity_signer_port() { // given let deps = Dependencies::new(); - let io1 = deps.with_signer(SignerService::new_test(Some(18180))); + let io1 = deps.with_signer(SignerService::new_test(Some(("127.0.0.1".into(), 18180)))); let io2 = deps.default_client(); // when @@ -313,6 +316,24 @@ fn rpc_parity_dapps_port() { assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned())); } +#[test] +fn rpc_parity_dapps_interface() { + // given + let mut deps = Dependencies::new(); + let io1 = deps.default_client(); + deps.dapps_interface = None; + let io2 = deps.default_client(); + + // when + let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsInterface", "params": [], "id": 1}"#; + let response1 = r#"{"jsonrpc":"2.0","result":"127.0.0.1","id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32031,"message":"Dapps Server is disabled. This API is not available.","data":null},"id":1}"#; + + // then + assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned())); + assert_eq!(io2.handle_request_sync(request), Some(response2.to_owned())); +} + #[test] fn rpc_parity_next_nonce() { let deps = Dependencies::new(); diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index 23cf50ed3..f8c219a89 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -123,6 +123,10 @@ build_rpc_trait! { #[rpc(name = "parity_dappsPort")] fn dapps_port(&self) -> Result; + /// Returns current Dapps Server interface address or an error if dapps server is disabled. + #[rpc(name = "parity_dappsInterface")] + fn dapps_interface(&self) -> Result; + /// Returns next nonce for particular sender. Should include all transactions in the queue. #[rpc(name = "parity_nextNonce")] fn next_nonce(&self, H160) -> Result;