Merge pull request #5992 from paritytech/csp-fix
Add missing CSP for web3.site
This commit is contained in:
commit
02f2c611d4
@ -25,6 +25,7 @@ use util::sha3::sha3;
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use handlers::{ContentValidator, ValidatorResponse};
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||
use Embeddable;
|
||||
|
||||
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
||||
|
||||
@ -116,16 +117,16 @@ pub struct Dapp {
|
||||
id: String,
|
||||
dapps_path: PathBuf,
|
||||
on_done: OnDone,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl Dapp {
|
||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable) -> Self {
|
||||
Dapp {
|
||||
id: id,
|
||||
dapps_path: dapps_path,
|
||||
on_done: on_done,
|
||||
embeddable_on: embeddable_on,
|
||||
id,
|
||||
dapps_path,
|
||||
on_done,
|
||||
embeddable_on,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ use parity_reactor::Remote;
|
||||
use hyper;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use {SyncStatus, random_filename};
|
||||
use {Embeddable, SyncStatus, random_filename};
|
||||
use util::Mutex;
|
||||
use page::LocalPageEndpoint;
|
||||
use handlers::{ContentHandler, ContentFetcherHandler};
|
||||
@ -52,7 +52,7 @@ pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHint
|
||||
resolver: R,
|
||||
cache: Arc<Mutex<ContentCache>>,
|
||||
sync: Arc<SyncStatus>,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
only_content: bool,
|
||||
@ -93,22 +93,22 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn embeddable_on(mut self, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
pub fn embeddable_on(mut self, embeddable_on: Embeddable) -> Self {
|
||||
self.embeddable_on = embeddable_on;
|
||||
self
|
||||
}
|
||||
|
||||
fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
|
||||
fn still_syncing(embeddable: Embeddable) -> Box<Handler> {
|
||||
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("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
||||
address,
|
||||
embeddable,
|
||||
))
|
||||
}
|
||||
|
||||
fn dapps_disabled(address: Option<(String, u16)>) -> Box<Handler> {
|
||||
fn dapps_disabled(address: Embeddable) -> Box<Handler> {
|
||||
Box::new(ContentHandler::error(
|
||||
StatusCode::ServiceUnavailable,
|
||||
"Network Dapps Not Available",
|
||||
|
@ -22,6 +22,7 @@ use std::path::{Path, PathBuf};
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use endpoint::{Endpoint, EndpointInfo};
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||
use Embeddable;
|
||||
|
||||
struct LocalDapp {
|
||||
id: String,
|
||||
@ -60,14 +61,14 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
||||
/// Parses the path to extract last component (for name).
|
||||
/// `None` is returned when path is invalid or non-existent.
|
||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, signer_address: Option<(String, u16)>) -> Option<(String, Box<LocalPageEndpoint>)> {
|
||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, embeddable: Embeddable) -> Option<(String, Box<LocalPageEndpoint>)> {
|
||||
let path = path.as_ref().to_owned();
|
||||
path.canonicalize().ok().and_then(|path| {
|
||||
let name = path.file_name().and_then(|name| name.to_str());
|
||||
name.map(|name| {
|
||||
let dapp = local_dapp(name.into(), path.clone());
|
||||
(dapp.id, Box::new(LocalPageEndpoint::new(
|
||||
dapp.path, dapp.info, PageCache::Disabled, signer_address.clone())
|
||||
dapp.path, dapp.info, PageCache::Disabled, embeddable.clone())
|
||||
))
|
||||
})
|
||||
})
|
||||
@ -86,12 +87,12 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
|
||||
|
||||
/// Returns endpoints for Local Dapps found for given filesystem path.
|
||||
/// Scans the directory and collects `LocalPageEndpoints`.
|
||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> BTreeMap<String, Box<Endpoint>> {
|
||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, embeddable: Embeddable) -> BTreeMap<String, Box<Endpoint>> {
|
||||
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
|
||||
for dapp in local_dapps(dapps_path.as_ref()) {
|
||||
pages.insert(
|
||||
dapp.id,
|
||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
|
||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, embeddable.clone()))
|
||||
);
|
||||
}
|
||||
pages
|
||||
|
@ -26,7 +26,7 @@ use fetch::Fetch;
|
||||
use parity_dapps::WebApp;
|
||||
use parity_reactor::Remote;
|
||||
use parity_ui;
|
||||
use {WebProxyTokens};
|
||||
use {WebProxyTokens, ParentFrameSettings};
|
||||
|
||||
mod app;
|
||||
mod cache;
|
||||
@ -52,23 +52,23 @@ pub fn ui() -> Box<Endpoint> {
|
||||
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
|
||||
}
|
||||
|
||||
pub fn ui_redirection(ui_address: Option<(String, u16)>) -> Box<Endpoint> {
|
||||
Box::new(ui::Redirection::new(ui_address))
|
||||
pub fn ui_redirection(embeddable: Option<ParentFrameSettings>) -> Box<Endpoint> {
|
||||
Box::new(ui::Redirection::new(embeddable))
|
||||
}
|
||||
|
||||
pub fn all_endpoints<F: Fetch>(
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: String,
|
||||
ui_address: Option<(String, u16)>,
|
||||
dapps_domain: &str,
|
||||
embeddable: Option<ParentFrameSettings>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Endpoints {
|
||||
// fetch fs dapps at first to avoid overwriting builtins
|
||||
let mut pages = fs::local_endpoints(dapps_path, ui_address.clone());
|
||||
let mut pages = fs::local_endpoints(dapps_path, embeddable.clone());
|
||||
for path in extra_dapps {
|
||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), ui_address.clone()) {
|
||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone()) {
|
||||
pages.insert(id, endpoint);
|
||||
} else {
|
||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
||||
@ -76,9 +76,9 @@ pub fn all_endpoints<F: Fetch>(
|
||||
}
|
||||
|
||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(ui_address.clone()));
|
||||
pages.insert("proxy".into(), ProxyPac::boxed(ui_address.clone(), dapps_domain));
|
||||
pages.insert(WEB_PATH.into(), Web::boxed(ui_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(embeddable.clone()));
|
||||
pages.insert("proxy".into(), ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned()));
|
||||
pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
||||
|
||||
Arc::new(pages)
|
||||
}
|
||||
@ -91,7 +91,7 @@ fn insert<T : WebApp + Default + 'static>(pages: &mut BTreeMap<String, Box<Endpo
|
||||
}
|
||||
|
||||
enum Embeddable {
|
||||
Yes(Option<(String, u16)>),
|
||||
Yes(Option<ParentFrameSettings>),
|
||||
#[allow(dead_code)]
|
||||
No,
|
||||
}
|
||||
|
@ -19,28 +19,28 @@
|
||||
use hyper::{Control, StatusCode};
|
||||
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use {address, handlers};
|
||||
use {handlers, Embeddable};
|
||||
|
||||
/// Redirection to UI server.
|
||||
pub struct Redirection {
|
||||
signer_address: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl Redirection {
|
||||
pub fn new(
|
||||
signer_address: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
) -> Self {
|
||||
Redirection {
|
||||
signer_address: signer_address,
|
||||
embeddable_on,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for Redirection {
|
||||
fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box<Handler> {
|
||||
if let Some(ref signer_address) = self.signer_address {
|
||||
if let Some(ref frame) = self.embeddable_on {
|
||||
trace!(target: "dapps", "Redirecting to signer interface.");
|
||||
handlers::Redirection::boxed(&format!("http://{}", address(signer_address)))
|
||||
handlers::Redirection::boxed(&format!("http://{}:{}", &frame.host, frame.port))
|
||||
} else {
|
||||
trace!(target: "dapps", "Signer disabled, returning 404.");
|
||||
Box::new(handlers::ContentHandler::error(
|
||||
@ -48,7 +48,7 @@ impl Endpoint for Redirection {
|
||||
"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-enable Signer to get your homepage back."),
|
||||
self.signer_address.clone(),
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ use hyper::status::StatusCode;
|
||||
use util::version;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
use Embeddable;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ContentHandler {
|
||||
@ -31,7 +32,7 @@ pub struct ContentHandler {
|
||||
content: String,
|
||||
mimetype: Mime,
|
||||
write_pos: usize,
|
||||
safe_to_embed_on: Option<(String, u16)>,
|
||||
safe_to_embed_on: Embeddable,
|
||||
}
|
||||
|
||||
impl ContentHandler {
|
||||
@ -39,11 +40,17 @@ impl ContentHandler {
|
||||
Self::new(StatusCode::Ok, content, mimetype)
|
||||
}
|
||||
|
||||
pub fn html(code: StatusCode, content: String, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
pub fn html(code: StatusCode, content: String, embeddable_on: Embeddable) -> Self {
|
||||
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
|
||||
}
|
||||
|
||||
pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
pub fn error(
|
||||
code: StatusCode,
|
||||
title: &str,
|
||||
message: &str,
|
||||
details: Option<&str>,
|
||||
embeddable_on: Embeddable,
|
||||
) -> Self {
|
||||
Self::html(code, format!(
|
||||
include_str!("../error_tpl.html"),
|
||||
title=title,
|
||||
@ -57,13 +64,18 @@ impl ContentHandler {
|
||||
Self::new_embeddable(code, content, mimetype, None)
|
||||
}
|
||||
|
||||
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
pub fn new_embeddable(
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
) -> Self {
|
||||
ContentHandler {
|
||||
code: code,
|
||||
content: content,
|
||||
mimetype: mimetype,
|
||||
code,
|
||||
content,
|
||||
mimetype,
|
||||
write_pos: 0,
|
||||
safe_to_embed_on: embeddable_on,
|
||||
safe_to_embed_on,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -80,7 +92,7 @@ impl server::Handler<HttpStream> 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_on.clone());
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ use hyper::status::StatusCode;
|
||||
use endpoint::EndpointPath;
|
||||
use handlers::{ContentHandler, StreamingHandler};
|
||||
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
||||
use {Embeddable};
|
||||
|
||||
const FETCH_TIMEOUT: u64 = 300;
|
||||
|
||||
@ -179,7 +180,7 @@ impl server::Handler<HttpStream> for WaitingHandler {
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Errors {
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl Errors {
|
||||
@ -241,20 +242,20 @@ impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
||||
path: EndpointPath,
|
||||
control: Control,
|
||||
installer: H,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
ContentFetcherHandler {
|
||||
fetch_control: FetchControl::default(),
|
||||
control: control,
|
||||
remote: remote,
|
||||
fetch: fetch,
|
||||
control,
|
||||
remote,
|
||||
fetch,
|
||||
status: FetchState::NotStarted(url),
|
||||
installer: Some(installer),
|
||||
path: path,
|
||||
path,
|
||||
errors: Errors {
|
||||
embeddable_on: embeddable_on,
|
||||
embeddable_on,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -30,22 +30,20 @@ pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, Val
|
||||
pub use self::redirect::Redirection;
|
||||
pub use self::streaming::StreamingHandler;
|
||||
|
||||
use std::iter;
|
||||
use util::Itertools;
|
||||
|
||||
use url::Url;
|
||||
use hyper::{server, header, net, uri};
|
||||
use address;
|
||||
use {apps, address, Embeddable};
|
||||
|
||||
/// Adds security-related headers to the Response.
|
||||
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option<(String, u16)>) {
|
||||
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embeddable) {
|
||||
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(ref embeddable_on) = embeddable_on {
|
||||
headers.set_raw("X-Frame-Options", vec![
|
||||
format!("ALLOW-FROM http://{}", address(embeddable_on)).into_bytes()
|
||||
]);
|
||||
} else {
|
||||
// TODO [ToDr] Should we be more strict here (DENY?)?
|
||||
if let None = embeddable_on {
|
||||
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||
}
|
||||
|
||||
@ -62,7 +60,7 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option
|
||||
b"child-src 'self' http: https:;".to_vec(),
|
||||
// We allow data: blob: and HTTP(s) images.
|
||||
// We could get rid of wildcarding HTTP and only allow RPC server URL.
|
||||
// (http require for local dapps icons)
|
||||
// (http required for local dapps icons)
|
||||
b"img-src 'self' 'unsafe-inline' data: blob: http: https:;".to_vec(),
|
||||
// Allow style from data: blob: and HTTPS.
|
||||
b"style-src 'self' 'unsafe-inline' data: blob: https:;".to_vec(),
|
||||
@ -80,10 +78,27 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option
|
||||
b"block-all-mixed-content;".to_vec(),
|
||||
// Specify if the site can be embedded.
|
||||
match embeddable_on {
|
||||
Some((ref host, ref port)) if host == "127.0.0.1" => {
|
||||
format!("frame-ancestors {} {};", address(&(host.to_owned(), *port)), address(&("localhost".to_owned(), *port)))
|
||||
Some(ref embed) => {
|
||||
let std = address(&embed.host, embed.port);
|
||||
let proxy = format!("{}.{}", apps::HOME_PAGE, embed.dapps_domain);
|
||||
let domain = format!("*.{}:{}", embed.dapps_domain, embed.port);
|
||||
|
||||
let mut ancestors = vec![std, domain, proxy]
|
||||
.into_iter()
|
||||
.chain(embed.extra_embed_on
|
||||
.iter()
|
||||
.map(|&(ref host, port)| format!("{}:{}", host, port))
|
||||
);
|
||||
|
||||
let ancestors = if embed.host == "127.0.0.1" {
|
||||
let localhost = address("localhost", embed.port);
|
||||
ancestors.chain(iter::once(localhost)).join(" ")
|
||||
} else {
|
||||
ancestors.join(" ")
|
||||
};
|
||||
|
||||
format!("frame-ancestors {};", ancestors)
|
||||
},
|
||||
Some(ref embed) => format!("frame-ancestors {};", address(embed)),
|
||||
None => format!("frame-ancestors 'self';"),
|
||||
}.into_bytes(),
|
||||
]);
|
||||
|
@ -24,6 +24,7 @@ use hyper::mime::Mime;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
use Embeddable;
|
||||
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
@ -33,11 +34,11 @@ pub struct StreamingHandler<R: io::Read> {
|
||||
status: StatusCode,
|
||||
content: io::BufReader<R>,
|
||||
mimetype: Mime,
|
||||
safe_to_embed_on: Option<(String, u16)>,
|
||||
safe_to_embed_on: Embeddable,
|
||||
}
|
||||
|
||||
impl<R: io::Read> StreamingHandler<R> {
|
||||
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Embeddable) -> Self {
|
||||
StreamingHandler {
|
||||
buffer: [0; BUFFER_SIZE],
|
||||
buffer_leftover: 0,
|
||||
@ -68,7 +69,7 @@ impl<R: io::Read> server::Handler<HttpStream> for StreamingHandler<R> {
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(self.status);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
|
@ -175,6 +175,7 @@ impl Middleware {
|
||||
pool: CpuPool,
|
||||
remote: Remote,
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: &str,
|
||||
@ -183,17 +184,18 @@ impl Middleware {
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
let embeddable = as_embeddable(ui_address, extra_embed_on, dapps_domain);
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||
sync_status.clone(),
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
).embeddable_on(ui_address.clone()).allow_dapps(true));
|
||||
).embeddable_on(embeddable.clone()).allow_dapps(true));
|
||||
let endpoints = apps::all_endpoints(
|
||||
dapps_path,
|
||||
extra_dapps,
|
||||
dapps_domain.to_owned(),
|
||||
ui_address.clone(),
|
||||
dapps_domain,
|
||||
embeddable.clone(),
|
||||
web_proxy_tokens,
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
@ -207,7 +209,10 @@ impl Middleware {
|
||||
remote.clone(),
|
||||
sync_status,
|
||||
);
|
||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui_redirection(ui_address.clone())));
|
||||
special.insert(
|
||||
router::SpecialEndpoint::Home,
|
||||
Some(apps::ui_redirection(embeddable.clone())),
|
||||
);
|
||||
special
|
||||
};
|
||||
|
||||
@ -215,7 +220,7 @@ impl Middleware {
|
||||
content_fetcher,
|
||||
Some(endpoints.clone()),
|
||||
special,
|
||||
ui_address,
|
||||
embeddable,
|
||||
dapps_domain.to_owned(),
|
||||
);
|
||||
|
||||
@ -251,8 +256,21 @@ fn special_endpoints(
|
||||
special
|
||||
}
|
||||
|
||||
fn address(address: &(String, u16)) -> String {
|
||||
format!("{}:{}", address.0, address.1)
|
||||
fn address(host: &str, port: u16) -> String {
|
||||
format!("{}:{}", host, port)
|
||||
}
|
||||
|
||||
fn as_embeddable(
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
dapps_domain: &str,
|
||||
) -> Option<ParentFrameSettings> {
|
||||
ui_address.map(|(host, port)| ParentFrameSettings {
|
||||
host,
|
||||
port,
|
||||
extra_embed_on,
|
||||
dapps_domain: dapps_domain.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Random filename
|
||||
@ -261,3 +279,18 @@ fn random_filename() -> String {
|
||||
let mut rng = ::rand::OsRng::new().unwrap();
|
||||
rng.gen_ascii_chars().take(12).collect()
|
||||
}
|
||||
|
||||
type Embeddable = Option<ParentFrameSettings>;
|
||||
|
||||
/// Parent frame host and port allowed to embed the content.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParentFrameSettings {
|
||||
/// Hostname
|
||||
pub host: String,
|
||||
/// Port
|
||||
pub port: u16,
|
||||
/// Additional pages the pages can be embedded on.
|
||||
pub extra_embed_on: Vec<(String, u16)>,
|
||||
/// Dapps Domain (web3.site)
|
||||
pub dapps_domain: String,
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use page::{handler, PageCache};
|
||||
use std::sync::Arc;
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||
use parity_dapps::{WebApp, File, Info};
|
||||
use Embeddable;
|
||||
|
||||
pub struct PageEndpoint<T : WebApp + 'static> {
|
||||
/// Content of the files
|
||||
@ -25,7 +26,7 @@ pub struct PageEndpoint<T : WebApp + 'static> {
|
||||
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||
pub prefix: Option<String>,
|
||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||
safe_to_embed_on: Option<(String, u16)>,
|
||||
safe_to_embed_on: Embeddable,
|
||||
info: EndpointInfo,
|
||||
fallback_to_index_html: bool,
|
||||
}
|
||||
@ -73,7 +74,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
/// 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, address: Option<(String, u16)>) -> Self {
|
||||
pub fn new_safe_to_embed(app: T, address: Embeddable) -> Self {
|
||||
let info = app.info();
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
|
@ -24,6 +24,7 @@ use hyper::status::StatusCode;
|
||||
use hyper::{Decoder, Encoder, Next};
|
||||
use endpoint::EndpointPath;
|
||||
use handlers::{ContentHandler, add_security_headers};
|
||||
use {Embeddable};
|
||||
|
||||
/// Represents a file that can be sent to client.
|
||||
/// Implementation should keep track of bytes already sent internally.
|
||||
@ -59,7 +60,7 @@ pub enum ServedFile<T: Dapp> {
|
||||
}
|
||||
|
||||
impl<T: Dapp> ServedFile<T> {
|
||||
pub fn new(embeddable_on: Option<(String, u16)>) -> Self {
|
||||
pub fn new(embeddable_on: Embeddable) -> Self {
|
||||
ServedFile::Error(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
@ -102,7 +103,7 @@ pub struct PageHandler<T: Dapp> {
|
||||
/// Requested path.
|
||||
pub path: EndpointPath,
|
||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||
pub safe_to_embed_on: Option<(String, u16)>,
|
||||
pub safe_to_embed_on: Embeddable,
|
||||
/// Cache settings for this page.
|
||||
pub cache: PageCache,
|
||||
}
|
||||
@ -174,7 +175,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
||||
}
|
||||
|
||||
// Security headers:
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||
Next::write()
|
||||
},
|
||||
ServedFile::Error(ref mut handler) => {
|
||||
|
@ -21,6 +21,7 @@ use std::path::{Path, PathBuf};
|
||||
use page::handler::{self, PageCache, PageHandlerWaiting};
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||
use mime::Mime;
|
||||
use Embeddable;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalPageEndpoint {
|
||||
@ -28,11 +29,11 @@ pub struct LocalPageEndpoint {
|
||||
mime: Option<Mime>,
|
||||
info: Option<EndpointInfo>,
|
||||
cache: PageCache,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl LocalPageEndpoint {
|
||||
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Option<(String, u16)>) -> Self {
|
||||
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self {
|
||||
LocalPageEndpoint {
|
||||
path: path,
|
||||
mime: None,
|
||||
|
@ -19,27 +19,24 @@
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use handlers::ContentHandler;
|
||||
use apps::HOME_PAGE;
|
||||
use address;
|
||||
use {address, Embeddable};
|
||||
|
||||
pub struct ProxyPac {
|
||||
signer_address: Option<(String, u16)>,
|
||||
embeddable: Embeddable,
|
||||
dapps_domain: String,
|
||||
}
|
||||
|
||||
impl ProxyPac {
|
||||
pub fn boxed(signer_address: Option<(String, u16)>, dapps_domain: String) -> Box<Endpoint> {
|
||||
Box::new(ProxyPac {
|
||||
signer_address: signer_address,
|
||||
dapps_domain: dapps_domain,
|
||||
})
|
||||
pub fn boxed(embeddable: Embeddable, dapps_domain: String) -> Box<Endpoint> {
|
||||
Box::new(ProxyPac { embeddable, dapps_domain })
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for ProxyPac {
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||
let signer = self.signer_address
|
||||
let ui = self.embeddable
|
||||
.as_ref()
|
||||
.map(address)
|
||||
.map(|ref parent| address(&parent.host, parent.port))
|
||||
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
||||
|
||||
let content = format!(
|
||||
@ -58,7 +55,7 @@ function FindProxyForURL(url, host) {{
|
||||
return "DIRECT";
|
||||
}}
|
||||
"#,
|
||||
HOME_PAGE, self.dapps_domain, path.host, path.port, signer);
|
||||
HOME_PAGE, self.dapps_domain, path.host, path.port, ui);
|
||||
|
||||
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use apps;
|
||||
use apps::fetcher::Fetcher;
|
||||
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
|
||||
use handlers;
|
||||
use Embeddable;
|
||||
|
||||
/// Special endpoints are accessible on every domain (every dapp)
|
||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||
@ -45,7 +46,7 @@ pub struct Router {
|
||||
endpoints: Option<Endpoints>,
|
||||
fetch: Arc<Fetcher>,
|
||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
dapps_domain: String,
|
||||
}
|
||||
|
||||
@ -148,7 +149,7 @@ impl Router {
|
||||
content_fetcher: Arc<Fetcher>,
|
||||
endpoints: Option<Endpoints>,
|
||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||
embeddable_on: Option<(String, u16)>,
|
||||
embeddable_on: Embeddable,
|
||||
dapps_domain: String,
|
||||
) -> Self {
|
||||
Router {
|
||||
|
@ -259,6 +259,7 @@ impl Server {
|
||||
CpuPool::new(4),
|
||||
remote,
|
||||
signer_address,
|
||||
vec![],
|
||||
dapps_path,
|
||||
extra_dapps,
|
||||
DAPPS_DOMAIN.into(),
|
||||
|
@ -31,9 +31,7 @@ use handlers::{
|
||||
StreamingHandler, extract_url,
|
||||
};
|
||||
use url::Url;
|
||||
use WebProxyTokens;
|
||||
|
||||
pub type Embeddable = Option<(String, u16)>;
|
||||
use {Embeddable, WebProxyTokens};
|
||||
|
||||
pub struct Web<F> {
|
||||
embeddable_on: Embeddable,
|
||||
@ -43,12 +41,17 @@ pub struct Web<F> {
|
||||
}
|
||||
|
||||
impl<F: Fetch> Web<F> {
|
||||
pub fn boxed(embeddable_on: Embeddable, web_proxy_tokens: Arc<WebProxyTokens>, remote: Remote, fetch: F) -> Box<Endpoint> {
|
||||
pub fn boxed(
|
||||
embeddable_on: Embeddable,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Box<Endpoint> {
|
||||
Box::new(Web {
|
||||
embeddable_on: embeddable_on,
|
||||
web_proxy_tokens: web_proxy_tokens,
|
||||
remote: remote,
|
||||
fetch: fetch,
|
||||
embeddable_on,
|
||||
web_proxy_tokens,
|
||||
remote,
|
||||
fetch,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -102,12 +102,7 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
||||
|
||||
/// Check if all required security headers are present
|
||||
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||
if let Some(port) = port {
|
||||
assert!(
|
||||
headers.iter().find(|header| header.as_str() == &format!("X-Frame-Options: ALLOW-FROM http://127.0.0.1:{}", port)).is_some(),
|
||||
"X-Frame-Options: ALLOW-FROM missing: {:?}", headers
|
||||
);
|
||||
} else {
|
||||
if let None = port {
|
||||
assert!(
|
||||
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
||||
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
||||
|
@ -573,6 +573,11 @@ impl Configuration {
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
extra_embed_on: if self.args.flag_ui_no_validation {
|
||||
vec![("localhost".to_owned(), 3000)]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -778,15 +783,11 @@ impl Configuration {
|
||||
}
|
||||
|
||||
fn ws_hosts(&self) -> Option<Vec<String>> {
|
||||
if self.args.flag_ui_no_validation {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.hosts(&self.args.flag_ws_hosts, &self.ws_interface())
|
||||
}
|
||||
|
||||
fn ws_origins(&self) -> Option<Vec<String>> {
|
||||
if self.args.flag_unsafe_expose {
|
||||
if self.args.flag_unsafe_expose || self.args.flag_ui_no_validation {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -1522,7 +1523,8 @@ mod tests {
|
||||
port: 8180,
|
||||
hosts: Some(vec![]),
|
||||
});
|
||||
assert_eq!(conf1.ws_config().unwrap().hosts, None);
|
||||
assert_eq!(conf1.dapps_config().extra_embed_on, vec![("localhost".to_owned(), 3000)]);
|
||||
assert_eq!(conf1.ws_config().unwrap().origins, None);
|
||||
assert_eq!(conf2.directories().signer, "signer".to_owned());
|
||||
assert_eq!(conf2.ui_config(), UiConfiguration {
|
||||
enabled: true,
|
||||
|
@ -39,6 +39,7 @@ pub struct Configuration {
|
||||
pub ntp_server: String,
|
||||
pub dapps_path: PathBuf,
|
||||
pub extra_dapps: Vec<PathBuf>,
|
||||
pub extra_embed_on: Vec<(String, u16)>,
|
||||
}
|
||||
|
||||
impl Default for Configuration {
|
||||
@ -49,6 +50,7 @@ impl Default for Configuration {
|
||||
ntp_server: "pool.ntp.org:123".into(),
|
||||
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
|
||||
extra_dapps: vec![],
|
||||
extra_embed_on: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -160,6 +162,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Mi
|
||||
configuration.dapps_path,
|
||||
configuration.extra_dapps,
|
||||
rpc::DAPPS_DOMAIN,
|
||||
configuration.extra_embed_on,
|
||||
).map(Some)
|
||||
}
|
||||
|
||||
@ -202,6 +205,7 @@ mod server {
|
||||
_dapps_path: PathBuf,
|
||||
_extra_dapps: Vec<PathBuf>,
|
||||
_dapps_domain: &str,
|
||||
_extra_embed_on: Vec<(String, u16)>,
|
||||
) -> Result<Middleware, String> {
|
||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
||||
}
|
||||
@ -238,6 +242,7 @@ mod server {
|
||||
dapps_path: PathBuf,
|
||||
extra_dapps: Vec<PathBuf>,
|
||||
dapps_domain: &str,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
) -> Result<Middleware, String> {
|
||||
let signer = deps.signer;
|
||||
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
|
||||
@ -248,6 +253,7 @@ mod server {
|
||||
deps.pool,
|
||||
parity_remote,
|
||||
deps.ui_address,
|
||||
extra_embed_on,
|
||||
dapps_path,
|
||||
extra_dapps,
|
||||
dapps_domain,
|
||||
|
Loading…
Reference in New Issue
Block a user