Merge remote-tracking branch 'origin/csp-fix'
This commit is contained in:
commit
f1dcdab75d
@ -25,6 +25,7 @@ use util::sha3::sha3;
|
|||||||
use page::{LocalPageEndpoint, PageCache};
|
use page::{LocalPageEndpoint, PageCache};
|
||||||
use handlers::{ContentValidator, ValidatorResponse};
|
use handlers::{ContentValidator, ValidatorResponse};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
||||||
|
|
||||||
@ -116,16 +117,16 @@ pub struct Dapp {
|
|||||||
id: String,
|
id: String,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
on_done: OnDone,
|
on_done: OnDone,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dapp {
|
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 {
|
Dapp {
|
||||||
id: id,
|
id,
|
||||||
dapps_path: dapps_path,
|
dapps_path,
|
||||||
on_done: on_done,
|
on_done,
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ use parity_reactor::Remote;
|
|||||||
use hyper;
|
use hyper;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use {SyncStatus, random_filename};
|
use {Embeddable, SyncStatus, random_filename};
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
use page::LocalPageEndpoint;
|
use page::LocalPageEndpoint;
|
||||||
use handlers::{ContentHandler, ContentFetcherHandler};
|
use handlers::{ContentHandler, ContentFetcherHandler};
|
||||||
@ -52,7 +52,7 @@ pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHint
|
|||||||
resolver: R,
|
resolver: R,
|
||||||
cache: Arc<Mutex<ContentCache>>,
|
cache: Arc<Mutex<ContentCache>>,
|
||||||
sync: Arc<SyncStatus>,
|
sync: Arc<SyncStatus>,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
only_content: bool,
|
only_content: bool,
|
||||||
@ -93,22 +93,22 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
|||||||
self
|
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.embeddable_on = embeddable_on;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
|
fn still_syncing(embeddable: Embeddable) -> Box<Handler> {
|
||||||
Box::new(ContentHandler::error(
|
Box::new(ContentHandler::error(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
"Sync In Progress",
|
"Sync In Progress",
|
||||||
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
||||||
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
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(
|
Box::new(ContentHandler::error(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
"Network Dapps Not Available",
|
"Network Dapps Not Available",
|
||||||
|
@ -22,6 +22,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use page::{LocalPageEndpoint, PageCache};
|
use page::{LocalPageEndpoint, PageCache};
|
||||||
use endpoint::{Endpoint, EndpointInfo};
|
use endpoint::{Endpoint, EndpointInfo};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
struct LocalDapp {
|
struct LocalDapp {
|
||||||
id: String,
|
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.
|
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
||||||
/// Parses the path to extract last component (for name).
|
/// Parses the path to extract last component (for name).
|
||||||
/// `None` is returned when path is invalid or non-existent.
|
/// `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();
|
let path = path.as_ref().to_owned();
|
||||||
path.canonicalize().ok().and_then(|path| {
|
path.canonicalize().ok().and_then(|path| {
|
||||||
let name = path.file_name().and_then(|name| name.to_str());
|
let name = path.file_name().and_then(|name| name.to_str());
|
||||||
name.map(|name| {
|
name.map(|name| {
|
||||||
let dapp = local_dapp(name.into(), path.clone());
|
let dapp = local_dapp(name.into(), path.clone());
|
||||||
(dapp.id, Box::new(LocalPageEndpoint::new(
|
(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.
|
/// Returns endpoints for Local Dapps found for given filesystem path.
|
||||||
/// Scans the directory and collects `LocalPageEndpoints`.
|
/// 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();
|
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
|
||||||
for dapp in local_dapps(dapps_path.as_ref()) {
|
for dapp in local_dapps(dapps_path.as_ref()) {
|
||||||
pages.insert(
|
pages.insert(
|
||||||
dapp.id,
|
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
|
pages
|
||||||
|
@ -26,7 +26,7 @@ use fetch::Fetch;
|
|||||||
use parity_dapps::WebApp;
|
use parity_dapps::WebApp;
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
use parity_ui;
|
use parity_ui;
|
||||||
use {WebProxyTokens};
|
use {WebProxyTokens, ParentFrameSettings, as_embeddable};
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod cache;
|
mod cache;
|
||||||
@ -52,8 +52,8 @@ pub fn ui() -> Box<Endpoint> {
|
|||||||
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
|
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui_redirection(ui_address: Option<(String, u16)>) -> Box<Endpoint> {
|
pub fn ui_redirection(ui_address: Option<(String, u16)>, dapps_domain: String) -> Box<Endpoint> {
|
||||||
Box::new(ui::Redirection::new(ui_address))
|
Box::new(ui::Redirection::new(as_embeddable(ui_address, dapps_domain)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_endpoints<F: Fetch>(
|
pub fn all_endpoints<F: Fetch>(
|
||||||
@ -65,10 +65,11 @@ pub fn all_endpoints<F: Fetch>(
|
|||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Endpoints {
|
) -> Endpoints {
|
||||||
|
let embeddable = as_embeddable(ui_address.clone(), dapps_domain.clone());
|
||||||
// fetch fs dapps at first to avoid overwriting builtins
|
// 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 {
|
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);
|
pages.insert(id, endpoint);
|
||||||
} else {
|
} else {
|
||||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
||||||
@ -76,9 +77,9 @@ pub fn all_endpoints<F: Fetch>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(ui_address.clone()));
|
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(embeddable.clone()));
|
||||||
pages.insert("proxy".into(), ProxyPac::boxed(ui_address.clone(), dapps_domain));
|
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()));
|
pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
||||||
|
|
||||||
Arc::new(pages)
|
Arc::new(pages)
|
||||||
}
|
}
|
||||||
@ -91,7 +92,7 @@ fn insert<T : WebApp + Default + 'static>(pages: &mut BTreeMap<String, Box<Endpo
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum Embeddable {
|
enum Embeddable {
|
||||||
Yes(Option<(String, u16)>),
|
Yes(Option<ParentFrameSettings>),
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
@ -19,28 +19,28 @@
|
|||||||
use hyper::{Control, StatusCode};
|
use hyper::{Control, StatusCode};
|
||||||
|
|
||||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||||
use {address, handlers};
|
use {handlers, Embeddable};
|
||||||
|
|
||||||
/// Redirection to UI server.
|
/// Redirection to UI server.
|
||||||
pub struct Redirection {
|
pub struct Redirection {
|
||||||
signer_address: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Redirection {
|
impl Redirection {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
signer_address: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Redirection {
|
Redirection {
|
||||||
signer_address: signer_address,
|
embeddable_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for Redirection {
|
impl Endpoint for Redirection {
|
||||||
fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box<Handler> {
|
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.");
|
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 {
|
} else {
|
||||||
trace!(target: "dapps", "Signer disabled, returning 404.");
|
trace!(target: "dapps", "Signer disabled, returning 404.");
|
||||||
Box::new(handlers::ContentHandler::error(
|
Box::new(handlers::ContentHandler::error(
|
||||||
@ -48,7 +48,7 @@ impl Endpoint for Redirection {
|
|||||||
"404 Not Found",
|
"404 Not Found",
|
||||||
"Your homepage is not available when Trusted Signer is disabled.",
|
"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."),
|
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 util::version;
|
||||||
|
|
||||||
use handlers::add_security_headers;
|
use handlers::add_security_headers;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ContentHandler {
|
pub struct ContentHandler {
|
||||||
@ -31,7 +32,7 @@ pub struct ContentHandler {
|
|||||||
content: String,
|
content: String,
|
||||||
mimetype: Mime,
|
mimetype: Mime,
|
||||||
write_pos: usize,
|
write_pos: usize,
|
||||||
safe_to_embed_on: Option<(String, u16)>,
|
safe_to_embed_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContentHandler {
|
impl ContentHandler {
|
||||||
@ -43,11 +44,17 @@ impl ContentHandler {
|
|||||||
Self::new(StatusCode::NotFound, content, mimetype)
|
Self::new(StatusCode::NotFound, 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)
|
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!(
|
Self::html(code, format!(
|
||||||
include_str!("../error_tpl.html"),
|
include_str!("../error_tpl.html"),
|
||||||
title=title,
|
title=title,
|
||||||
@ -61,13 +68,18 @@ impl ContentHandler {
|
|||||||
Self::new_embeddable(code, content, mimetype, None)
|
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 {
|
ContentHandler {
|
||||||
code: code,
|
code,
|
||||||
content: content,
|
content,
|
||||||
mimetype: mimetype,
|
mimetype,
|
||||||
write_pos: 0,
|
write_pos: 0,
|
||||||
safe_to_embed_on: embeddable_on,
|
safe_to_embed_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +96,7 @@ impl server::Handler<HttpStream> for ContentHandler {
|
|||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
res.set_status(self.code);
|
res.set_status(self.code);
|
||||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
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()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ use hyper::status::StatusCode;
|
|||||||
use endpoint::EndpointPath;
|
use endpoint::EndpointPath;
|
||||||
use handlers::{ContentHandler, StreamingHandler};
|
use handlers::{ContentHandler, StreamingHandler};
|
||||||
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
||||||
|
use {Embeddable};
|
||||||
|
|
||||||
const FETCH_TIMEOUT: u64 = 300;
|
const FETCH_TIMEOUT: u64 = 300;
|
||||||
|
|
||||||
@ -179,7 +180,7 @@ impl server::Handler<HttpStream> for WaitingHandler {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Errors {
|
struct Errors {
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Errors {
|
impl Errors {
|
||||||
@ -241,20 +242,20 @@ impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
|||||||
path: EndpointPath,
|
path: EndpointPath,
|
||||||
control: Control,
|
control: Control,
|
||||||
installer: H,
|
installer: H,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ContentFetcherHandler {
|
ContentFetcherHandler {
|
||||||
fetch_control: FetchControl::default(),
|
fetch_control: FetchControl::default(),
|
||||||
control: control,
|
control,
|
||||||
remote: remote,
|
remote,
|
||||||
fetch: fetch,
|
fetch,
|
||||||
status: FetchState::NotStarted(url),
|
status: FetchState::NotStarted(url),
|
||||||
installer: Some(installer),
|
installer: Some(installer),
|
||||||
path: path,
|
path,
|
||||||
errors: Errors {
|
errors: Errors {
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,20 +30,15 @@ pub use self::streaming::StreamingHandler;
|
|||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use hyper::{server, header, net, uri};
|
use hyper::{server, header, net, uri};
|
||||||
use address;
|
use {apps, address, Embeddable};
|
||||||
|
|
||||||
/// Adds security-related headers to the Response.
|
/// 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-XSS-Protection", vec![b"1; mode=block".to_vec()]);
|
||||||
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
||||||
|
|
||||||
// Embedding header:
|
// Embedding header:
|
||||||
if let Some(ref embeddable_on) = embeddable_on {
|
if let None = 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?)?
|
|
||||||
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +55,7 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option
|
|||||||
b"child-src 'self' http: https:;".to_vec(),
|
b"child-src 'self' http: https:;".to_vec(),
|
||||||
// We allow data: blob: and HTTP(s) images.
|
// We allow data: blob: and HTTP(s) images.
|
||||||
// We could get rid of wildcarding HTTP and only allow RPC server URL.
|
// 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(),
|
b"img-src 'self' 'unsafe-inline' data: blob: http: https:;".to_vec(),
|
||||||
// Allow style from data: blob: and HTTPS.
|
// Allow style from data: blob: and HTTPS.
|
||||||
b"style-src 'self' 'unsafe-inline' data: blob: https:;".to_vec(),
|
b"style-src 'self' 'unsafe-inline' data: blob: https:;".to_vec(),
|
||||||
@ -78,10 +73,27 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option
|
|||||||
b"block-all-mixed-content;".to_vec(),
|
b"block-all-mixed-content;".to_vec(),
|
||||||
// Specify if the site can be embedded.
|
// Specify if the site can be embedded.
|
||||||
match embeddable_on {
|
match embeddable_on {
|
||||||
Some((ref host, ref port)) if host == "127.0.0.1" => {
|
Some(ref embed) => {
|
||||||
format!("frame-ancestors {} {};", address(&(host.to_owned(), *port)), address(&("localhost".to_owned(), *port)))
|
let std = address(&embed.host, embed.port);
|
||||||
|
let proxy = format!("{}.{}", apps::HOME_PAGE, embed.dapps_domain);
|
||||||
|
let domain = format!("*.{}:{}", embed.dapps_domain, embed.port);
|
||||||
|
|
||||||
|
if embed.host == "127.0.0.1" {
|
||||||
|
let localhost = address("localhost", embed.port);
|
||||||
|
format!("frame-ancestors {} {} {} {};",
|
||||||
|
std,
|
||||||
|
localhost,
|
||||||
|
domain,
|
||||||
|
proxy,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("frame-ancestors {} {} {};",
|
||||||
|
std,
|
||||||
|
domain,
|
||||||
|
proxy,
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Some(ref embed) => format!("frame-ancestors {};", address(embed)),
|
|
||||||
None => format!("frame-ancestors 'self';"),
|
None => format!("frame-ancestors 'self';"),
|
||||||
}.into_bytes(),
|
}.into_bytes(),
|
||||||
]);
|
]);
|
||||||
|
@ -24,6 +24,7 @@ use hyper::mime::Mime;
|
|||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use handlers::add_security_headers;
|
use handlers::add_security_headers;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 1024;
|
const BUFFER_SIZE: usize = 1024;
|
||||||
|
|
||||||
@ -33,11 +34,11 @@ pub struct StreamingHandler<R: io::Read> {
|
|||||||
status: StatusCode,
|
status: StatusCode,
|
||||||
content: io::BufReader<R>,
|
content: io::BufReader<R>,
|
||||||
mimetype: Mime,
|
mimetype: Mime,
|
||||||
safe_to_embed_on: Option<(String, u16)>,
|
safe_to_embed_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: io::Read> StreamingHandler<R> {
|
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 {
|
StreamingHandler {
|
||||||
buffer: [0; BUFFER_SIZE],
|
buffer: [0; BUFFER_SIZE],
|
||||||
buffer_leftover: 0,
|
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 {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
res.set_status(self.status);
|
res.set_status(self.status);
|
||||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
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()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,12 +171,13 @@ impl Middleware {
|
|||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let embeddable = as_embeddable(ui_address.clone(), dapps_domain.clone());
|
||||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||||
sync_status,
|
sync_status,
|
||||||
remote.clone(),
|
remote.clone(),
|
||||||
fetch.clone(),
|
fetch.clone(),
|
||||||
).embeddable_on(ui_address.clone()).allow_dapps(true));
|
).embeddable_on(embeddable.clone()).allow_dapps(true));
|
||||||
let endpoints = apps::all_endpoints(
|
let endpoints = apps::all_endpoints(
|
||||||
dapps_path,
|
dapps_path,
|
||||||
extra_dapps,
|
extra_dapps,
|
||||||
@ -189,7 +190,10 @@ impl Middleware {
|
|||||||
|
|
||||||
let special = {
|
let special = {
|
||||||
let mut special = special_endpoints(content_fetcher.clone());
|
let mut special = special_endpoints(content_fetcher.clone());
|
||||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui_redirection(ui_address.clone())));
|
special.insert(
|
||||||
|
router::SpecialEndpoint::Home,
|
||||||
|
Some(apps::ui_redirection(ui_address.clone(), dapps_domain.clone())),
|
||||||
|
);
|
||||||
special
|
special
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -197,7 +201,7 @@ impl Middleware {
|
|||||||
content_fetcher,
|
content_fetcher,
|
||||||
Some(endpoints.clone()),
|
Some(endpoints.clone()),
|
||||||
special,
|
special,
|
||||||
ui_address,
|
embeddable,
|
||||||
dapps_domain,
|
dapps_domain,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -222,8 +226,12 @@ fn special_endpoints(content_fetcher: Arc<apps::fetcher::Fetcher>) -> HashMap<ro
|
|||||||
special
|
special
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address(address: &(String, u16)) -> String {
|
fn address(host: &str, port: u16) -> String {
|
||||||
format!("{}:{}", address.0, address.1)
|
format!("{}:{}", host, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_embeddable(ui_address: Option<(String, u16)>, dapps_domain: String) -> Option<ParentFrameSettings> {
|
||||||
|
ui_address.map(|(host, port)| ParentFrameSettings { host, port, dapps_domain, })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Random filename
|
/// Random filename
|
||||||
@ -232,3 +240,16 @@ fn random_filename() -> String {
|
|||||||
let mut rng = ::rand::OsRng::new().unwrap();
|
let mut rng = ::rand::OsRng::new().unwrap();
|
||||||
rng.gen_ascii_chars().take(12).collect()
|
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,
|
||||||
|
/// Dapps Domain (web3.site)
|
||||||
|
pub dapps_domain: String,
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ use page::{handler, PageCache};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||||
use parity_dapps::{WebApp, File, Info};
|
use parity_dapps::{WebApp, File, Info};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
pub struct PageEndpoint<T : WebApp + 'static> {
|
pub struct PageEndpoint<T : WebApp + 'static> {
|
||||||
/// Content of the files
|
/// 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`)
|
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||||
pub prefix: Option<String>,
|
pub prefix: Option<String>,
|
||||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
/// 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,
|
info: EndpointInfo,
|
||||||
fallback_to_index_html: bool,
|
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
|
/// Creates new `PageEndpoint` which can be safely used in iframe
|
||||||
/// even from different origin. It might be dangerous (clickjacking).
|
/// even from different origin. It might be dangerous (clickjacking).
|
||||||
/// Use wisely!
|
/// 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();
|
let info = app.info();
|
||||||
PageEndpoint {
|
PageEndpoint {
|
||||||
app: Arc::new(app),
|
app: Arc::new(app),
|
||||||
|
@ -24,6 +24,7 @@ use hyper::status::StatusCode;
|
|||||||
use hyper::{Decoder, Encoder, Next};
|
use hyper::{Decoder, Encoder, Next};
|
||||||
use endpoint::EndpointPath;
|
use endpoint::EndpointPath;
|
||||||
use handlers::{ContentHandler, add_security_headers};
|
use handlers::{ContentHandler, add_security_headers};
|
||||||
|
use {Embeddable};
|
||||||
|
|
||||||
/// Represents a file that can be sent to client.
|
/// Represents a file that can be sent to client.
|
||||||
/// Implementation should keep track of bytes already sent internally.
|
/// Implementation should keep track of bytes already sent internally.
|
||||||
@ -59,7 +60,7 @@ pub enum ServedFile<T: Dapp> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Dapp> ServedFile<T> {
|
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(
|
ServedFile::Error(ContentHandler::error(
|
||||||
StatusCode::NotFound,
|
StatusCode::NotFound,
|
||||||
"404 Not Found",
|
"404 Not Found",
|
||||||
@ -102,7 +103,7 @@ pub struct PageHandler<T: Dapp> {
|
|||||||
/// Requested path.
|
/// Requested path.
|
||||||
pub path: EndpointPath,
|
pub path: EndpointPath,
|
||||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
/// 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.
|
/// Cache settings for this page.
|
||||||
pub cache: PageCache,
|
pub cache: PageCache,
|
||||||
}
|
}
|
||||||
@ -174,7 +175,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Security headers:
|
// 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()
|
Next::write()
|
||||||
},
|
},
|
||||||
ServedFile::Error(ref mut handler) => {
|
ServedFile::Error(ref mut handler) => {
|
||||||
|
@ -21,6 +21,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use page::handler::{self, PageCache, PageHandlerWaiting};
|
use page::handler::{self, PageCache, PageHandlerWaiting};
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LocalPageEndpoint {
|
pub struct LocalPageEndpoint {
|
||||||
@ -28,11 +29,11 @@ pub struct LocalPageEndpoint {
|
|||||||
mime: Option<Mime>,
|
mime: Option<Mime>,
|
||||||
info: Option<EndpointInfo>,
|
info: Option<EndpointInfo>,
|
||||||
cache: PageCache,
|
cache: PageCache,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalPageEndpoint {
|
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 {
|
LocalPageEndpoint {
|
||||||
path: path,
|
path: path,
|
||||||
mime: None,
|
mime: None,
|
||||||
|
@ -39,7 +39,7 @@ impl Endpoint for ProxyPac {
|
|||||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||||
let signer = self.signer_address
|
let signer = self.signer_address
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(address)
|
.map(|&(ref host, port)| address(host, port))
|
||||||
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
|
@ -30,6 +30,7 @@ use apps;
|
|||||||
use apps::fetcher::Fetcher;
|
use apps::fetcher::Fetcher;
|
||||||
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
|
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
|
||||||
use handlers;
|
use handlers;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
/// Special endpoints are accessible on every domain (every dapp)
|
/// Special endpoints are accessible on every domain (every dapp)
|
||||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||||
@ -45,7 +46,7 @@ pub struct Router {
|
|||||||
endpoints: Option<Endpoints>,
|
endpoints: Option<Endpoints>,
|
||||||
fetch: Arc<Fetcher>,
|
fetch: Arc<Fetcher>,
|
||||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
dapps_domain: String,
|
dapps_domain: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +149,7 @@ impl Router {
|
|||||||
content_fetcher: Arc<Fetcher>,
|
content_fetcher: Arc<Fetcher>,
|
||||||
endpoints: Option<Endpoints>,
|
endpoints: Option<Endpoints>,
|
||||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
dapps_domain: String,
|
dapps_domain: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Router {
|
Router {
|
||||||
|
@ -31,9 +31,7 @@ use handlers::{
|
|||||||
StreamingHandler, extract_url,
|
StreamingHandler, extract_url,
|
||||||
};
|
};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use WebProxyTokens;
|
use {Embeddable, WebProxyTokens};
|
||||||
|
|
||||||
pub type Embeddable = Option<(String, u16)>;
|
|
||||||
|
|
||||||
pub struct Web<F> {
|
pub struct Web<F> {
|
||||||
embeddable_on: Embeddable,
|
embeddable_on: Embeddable,
|
||||||
@ -43,12 +41,17 @@ pub struct Web<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Fetch> 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 {
|
Box::new(Web {
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
web_proxy_tokens: web_proxy_tokens,
|
web_proxy_tokens,
|
||||||
remote: remote,
|
remote,
|
||||||
fetch: fetch,
|
fetch,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,12 +102,7 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
|||||||
|
|
||||||
/// Check if all required security headers are present
|
/// Check if all required security headers are present
|
||||||
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||||
if let Some(port) = port {
|
if let None = 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 {
|
|
||||||
assert!(
|
assert!(
|
||||||
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
||||||
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
||||||
|
Loading…
Reference in New Issue
Block a user