Allow specifying extra cors headers for dapps (#4710)
This commit is contained in:
parent
ae3f85bd5b
commit
4868f758bf
@ -39,7 +39,11 @@ pub struct RestApi {
|
|||||||
impl RestApi {
|
impl RestApi {
|
||||||
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
|
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
|
||||||
Box::new(RestApi {
|
Box::new(RestApi {
|
||||||
cors_domains: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
|
cors_domains: Some(cors_domains.into_iter().map(|domain| match domain.as_ref() {
|
||||||
|
"all" | "*" | "any" => AccessControlAllowOrigin::Any,
|
||||||
|
"null" => AccessControlAllowOrigin::Null,
|
||||||
|
other => AccessControlAllowOrigin::Value(other.into()),
|
||||||
|
}).collect()),
|
||||||
endpoints: endpoints,
|
endpoints: endpoints,
|
||||||
fetcher: fetcher,
|
fetcher: fetcher,
|
||||||
})
|
})
|
||||||
|
@ -111,6 +111,7 @@ pub struct ServerBuilder<T: Fetch = FetchClient> {
|
|||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
signer_address: Option<(String, u16)>,
|
signer_address: Option<(String, u16)>,
|
||||||
allowed_hosts: Option<Vec<String>>,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
|
extra_cors: Option<Vec<String>>,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: Option<T>,
|
fetch: Option<T>,
|
||||||
}
|
}
|
||||||
@ -126,6 +127,7 @@ impl ServerBuilder {
|
|||||||
web_proxy_tokens: Arc::new(|_| false),
|
web_proxy_tokens: Arc::new(|_| false),
|
||||||
signer_address: None,
|
signer_address: None,
|
||||||
allowed_hosts: Some(vec![]),
|
allowed_hosts: Some(vec![]),
|
||||||
|
extra_cors: None,
|
||||||
remote: remote,
|
remote: remote,
|
||||||
fetch: None,
|
fetch: None,
|
||||||
}
|
}
|
||||||
@ -143,6 +145,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
|||||||
web_proxy_tokens: self.web_proxy_tokens,
|
web_proxy_tokens: self.web_proxy_tokens,
|
||||||
signer_address: self.signer_address,
|
signer_address: self.signer_address,
|
||||||
allowed_hosts: self.allowed_hosts,
|
allowed_hosts: self.allowed_hosts,
|
||||||
|
extra_cors: self.extra_cors,
|
||||||
remote: self.remote,
|
remote: self.remote,
|
||||||
fetch: Some(fetch),
|
fetch: Some(fetch),
|
||||||
}
|
}
|
||||||
@ -174,6 +177,13 @@ impl<T: Fetch> ServerBuilder<T> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extra cors headers.
|
||||||
|
/// `None` - no additional CORS URLs
|
||||||
|
pub fn extra_cors_headers(mut self, cors: Option<Vec<String>>) -> Self {
|
||||||
|
self.extra_cors = cors;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Change extra dapps paths (apart from `dapps_path`)
|
/// Change extra dapps paths (apart from `dapps_path`)
|
||||||
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
|
pub fn extra_dapps<P: AsRef<Path>>(mut self, extra_dapps: &[P]) -> Self {
|
||||||
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
|
self.extra_dapps = extra_dapps.iter().map(|p| p.as_ref().to_owned()).collect();
|
||||||
@ -187,6 +197,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
|||||||
Server::start_http(
|
Server::start_http(
|
||||||
addr,
|
addr,
|
||||||
self.allowed_hosts,
|
self.allowed_hosts,
|
||||||
|
self.extra_cors,
|
||||||
NoAuth,
|
NoAuth,
|
||||||
handler,
|
handler,
|
||||||
self.dapps_path,
|
self.dapps_path,
|
||||||
@ -207,6 +218,7 @@ impl<T: Fetch> ServerBuilder<T> {
|
|||||||
Server::start_http(
|
Server::start_http(
|
||||||
addr,
|
addr,
|
||||||
self.allowed_hosts,
|
self.allowed_hosts,
|
||||||
|
self.extra_cors,
|
||||||
HttpBasicAuth::single_user(username, password),
|
HttpBasicAuth::single_user(username, password),
|
||||||
handler,
|
handler,
|
||||||
self.dapps_path,
|
self.dapps_path,
|
||||||
@ -251,8 +263,8 @@ impl Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of CORS domains for API endpoint.
|
/// Returns a list of CORS domains for API endpoint.
|
||||||
fn cors_domains(signer_address: Option<(String, u16)>) -> Vec<String> {
|
fn cors_domains(signer_address: Option<(String, u16)>, extra_cors: Option<Vec<String>>) -> Vec<String> {
|
||||||
match signer_address {
|
let basic_cors = match signer_address {
|
||||||
Some(signer_address) => vec![
|
Some(signer_address) => vec![
|
||||||
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||||
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
||||||
@ -260,15 +272,20 @@ impl Server {
|
|||||||
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
|
||||||
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
|
||||||
format!("https://{}", address(&signer_address)),
|
format!("https://{}", address(&signer_address)),
|
||||||
|
|
||||||
],
|
],
|
||||||
None => vec![],
|
None => vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
match extra_cors {
|
||||||
|
None => basic_cors,
|
||||||
|
Some(extra_cors) => basic_cors.into_iter().chain(extra_cors).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
|
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
|
||||||
addr: &SocketAddr,
|
addr: &SocketAddr,
|
||||||
hosts: Option<Vec<String>>,
|
hosts: Option<Vec<String>>,
|
||||||
|
extra_cors: Option<Vec<String>>,
|
||||||
authorization: A,
|
authorization: A,
|
||||||
handler: RpcHandler<Metadata, T>,
|
handler: RpcHandler<Metadata, T>,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
@ -297,7 +314,7 @@ impl Server {
|
|||||||
remote.clone(),
|
remote.clone(),
|
||||||
fetch.clone(),
|
fetch.clone(),
|
||||||
));
|
));
|
||||||
let cors_domains = Self::cors_domains(signer_address.clone());
|
let cors_domains = Self::cors_domains(signer_address.clone(), extra_cors);
|
||||||
|
|
||||||
let special = Arc::new({
|
let special = Arc::new({
|
||||||
let mut special = HashMap::new();
|
let mut special = HashMap::new();
|
||||||
@ -413,8 +430,9 @@ mod util_tests {
|
|||||||
// given
|
// given
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let none = Server::cors_domains(None);
|
let none = Server::cors_domains(None, None);
|
||||||
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)));
|
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)), None);
|
||||||
|
let extra = Server::cors_domains(None, Some(vec!["all".to_owned()]));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(none, Vec::<String>::new());
|
assert_eq!(none, Vec::<String>::new());
|
||||||
@ -425,7 +443,7 @@ mod util_tests {
|
|||||||
"https://parity.web3.site".into(),
|
"https://parity.web3.site".into(),
|
||||||
"https://parity.web3.site:18180".into(),
|
"https://parity.web3.site:18180".into(),
|
||||||
"https://127.0.0.1:18180".into()
|
"https://127.0.0.1:18180".into()
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
assert_eq!(extra, vec!["all".to_owned()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use tests::helpers::{serve, serve_with_registrar, request, assert_security_headers};
|
use tests::helpers::{serve, serve_with_registrar, serve_extra_cors, request, assert_security_headers};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_error() {
|
fn should_return_error() {
|
||||||
@ -212,3 +212,25 @@ fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_extra_cors_headers() {
|
||||||
|
// given
|
||||||
|
let server = serve_extra_cors(Some(vec!["all".to_owned()]));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let response = request(server,
|
||||||
|
"\
|
||||||
|
POST /api/ping HTTP/1.1\r\n\
|
||||||
|
Host: localhost:8080\r\n\
|
||||||
|
Origin: http://somedomain.io\r\n\
|
||||||
|
Connection: close\r\n\
|
||||||
|
\r\n\
|
||||||
|
{}
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// then
|
||||||
|
response.assert_status("HTTP/1.1 200 OK");
|
||||||
|
response.assert_header("Access-Control-Allow-Origin", "http://somedomain.io");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -109,6 +109,10 @@ pub fn serve_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
|
|||||||
init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0
|
init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn serve_extra_cors(extra_cors: Option<Vec<String>>) -> ServerLoop {
|
||||||
|
init_server(|builder| builder.allowed_hosts(None).extra_cors_headers(extra_cors), Default::default(), Remote::new_sync()).0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
|
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
|
||||||
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync())
|
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync())
|
||||||
}
|
}
|
||||||
|
@ -181,6 +181,8 @@ usage! {
|
|||||||
or |c: &Config| otry!(c.dapps).interface.clone(),
|
or |c: &Config| otry!(c.dapps).interface.clone(),
|
||||||
flag_dapps_hosts: String = "none",
|
flag_dapps_hosts: String = "none",
|
||||||
or |c: &Config| otry!(c.dapps).hosts.as_ref().map(|vec| vec.join(",")),
|
or |c: &Config| otry!(c.dapps).hosts.as_ref().map(|vec| vec.join(",")),
|
||||||
|
flag_dapps_cors: Option<String> = None,
|
||||||
|
or |c: &Config| otry!(c.dapps).cors.clone().map(Some),
|
||||||
flag_dapps_path: String = "$BASE/dapps",
|
flag_dapps_path: String = "$BASE/dapps",
|
||||||
or |c: &Config| otry!(c.dapps).path.clone(),
|
or |c: &Config| otry!(c.dapps).path.clone(),
|
||||||
flag_dapps_user: Option<String> = None,
|
flag_dapps_user: Option<String> = None,
|
||||||
@ -428,6 +430,7 @@ struct Dapps {
|
|||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
interface: Option<String>,
|
interface: Option<String>,
|
||||||
hosts: Option<Vec<String>>,
|
hosts: Option<Vec<String>>,
|
||||||
|
cors: Option<String>,
|
||||||
path: Option<String>,
|
path: Option<String>,
|
||||||
user: Option<String>,
|
user: Option<String>,
|
||||||
pass: Option<String>,
|
pass: Option<String>,
|
||||||
@ -674,6 +677,7 @@ mod tests {
|
|||||||
flag_dapps_port: 8080u16,
|
flag_dapps_port: 8080u16,
|
||||||
flag_dapps_interface: "local".into(),
|
flag_dapps_interface: "local".into(),
|
||||||
flag_dapps_hosts: "none".into(),
|
flag_dapps_hosts: "none".into(),
|
||||||
|
flag_dapps_cors: None,
|
||||||
flag_dapps_path: "$HOME/.parity/dapps".into(),
|
flag_dapps_path: "$HOME/.parity/dapps".into(),
|
||||||
flag_dapps_user: Some("test_user".into()),
|
flag_dapps_user: Some("test_user".into()),
|
||||||
flag_dapps_pass: Some("test_pass".into()),
|
flag_dapps_pass: Some("test_pass".into()),
|
||||||
@ -873,6 +877,7 @@ mod tests {
|
|||||||
path: None,
|
path: None,
|
||||||
interface: None,
|
interface: None,
|
||||||
hosts: None,
|
hosts: None,
|
||||||
|
cors: None,
|
||||||
user: Some("username".into()),
|
user: Some("username".into()),
|
||||||
pass: Some("password".into())
|
pass: Some("password".into())
|
||||||
}),
|
}),
|
||||||
|
@ -164,6 +164,8 @@ API and Console Options:
|
|||||||
is additional security against some attack
|
is additional security against some attack
|
||||||
vectors. Special options: "all", "none",
|
vectors. Special options: "all", "none",
|
||||||
(default: {flag_dapps_hosts}).
|
(default: {flag_dapps_hosts}).
|
||||||
|
--dapps-cors URL Specify CORS headers for Dapps server APIs.
|
||||||
|
(default: {flag_dapps_cors:?})
|
||||||
--dapps-user USERNAME Specify username for Dapps server. It will be
|
--dapps-user USERNAME Specify username for Dapps server. It will be
|
||||||
used in HTTP Basic Authentication Scheme.
|
used in HTTP Basic Authentication Scheme.
|
||||||
If --dapps-pass is not specified you will be
|
If --dapps-pass is not specified you will be
|
||||||
|
@ -546,6 +546,7 @@ impl Configuration {
|
|||||||
interface: self.dapps_interface(),
|
interface: self.dapps_interface(),
|
||||||
port: self.args.flag_dapps_port,
|
port: self.args.flag_dapps_port,
|
||||||
hosts: self.dapps_hosts(),
|
hosts: self.dapps_hosts(),
|
||||||
|
cors: self.dapps_cors(),
|
||||||
user: self.args.flag_dapps_user.clone(),
|
user: self.args.flag_dapps_user.clone(),
|
||||||
pass: self.args.flag_dapps_pass.clone(),
|
pass: self.args.flag_dapps_pass.clone(),
|
||||||
dapps_path: PathBuf::from(self.directories().dapps),
|
dapps_path: PathBuf::from(self.directories().dapps),
|
||||||
@ -722,6 +723,10 @@ impl Configuration {
|
|||||||
Self::cors(self.args.flag_ipfs_api_cors.as_ref())
|
Self::cors(self.args.flag_ipfs_api_cors.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dapps_cors(&self) -> Option<Vec<String>> {
|
||||||
|
Self::cors(self.args.flag_dapps_cors.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
fn hosts(hosts: &str) -> Option<Vec<String>> {
|
fn hosts(hosts: &str) -> Option<Vec<String>> {
|
||||||
match hosts {
|
match hosts {
|
||||||
"none" => return Some(Vec::new()),
|
"none" => return Some(Vec::new()),
|
||||||
|
@ -33,6 +33,7 @@ pub struct Configuration {
|
|||||||
pub interface: String,
|
pub interface: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub hosts: Option<Vec<String>>,
|
pub hosts: Option<Vec<String>>,
|
||||||
|
pub cors: Option<Vec<String>>,
|
||||||
pub user: Option<String>,
|
pub user: Option<String>,
|
||||||
pub pass: Option<String>,
|
pub pass: Option<String>,
|
||||||
pub dapps_path: PathBuf,
|
pub dapps_path: PathBuf,
|
||||||
@ -48,6 +49,7 @@ impl Default for Configuration {
|
|||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
port: 8080,
|
port: 8080,
|
||||||
hosts: Some(Vec::new()),
|
hosts: Some(Vec::new()),
|
||||||
|
cors: None,
|
||||||
user: None,
|
user: None,
|
||||||
pass: None,
|
pass: None,
|
||||||
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
|
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
|
||||||
@ -93,6 +95,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<We
|
|||||||
configuration.extra_dapps,
|
configuration.extra_dapps,
|
||||||
&addr,
|
&addr,
|
||||||
configuration.hosts,
|
configuration.hosts,
|
||||||
|
configuration.cors,
|
||||||
auth,
|
auth,
|
||||||
configuration.all_apis,
|
configuration.all_apis,
|
||||||
)?))
|
)?))
|
||||||
@ -114,6 +117,7 @@ mod server {
|
|||||||
_extra_dapps: Vec<PathBuf>,
|
_extra_dapps: Vec<PathBuf>,
|
||||||
_url: &SocketAddr,
|
_url: &SocketAddr,
|
||||||
_allowed_hosts: Option<Vec<String>>,
|
_allowed_hosts: Option<Vec<String>>,
|
||||||
|
_cors: Option<Vec<String>>,
|
||||||
_auth: Option<(String, String)>,
|
_auth: Option<(String, String)>,
|
||||||
_all_apis: bool,
|
_all_apis: bool,
|
||||||
) -> Result<WebappServer, String> {
|
) -> Result<WebappServer, String> {
|
||||||
@ -147,6 +151,7 @@ mod server {
|
|||||||
extra_dapps: Vec<PathBuf>,
|
extra_dapps: Vec<PathBuf>,
|
||||||
url: &SocketAddr,
|
url: &SocketAddr,
|
||||||
allowed_hosts: Option<Vec<String>>,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
|
cors: Option<Vec<String>>,
|
||||||
auth: Option<(String, String)>,
|
auth: Option<(String, String)>,
|
||||||
all_apis: bool,
|
all_apis: bool,
|
||||||
) -> Result<WebappServer, String> {
|
) -> Result<WebappServer, String> {
|
||||||
@ -167,7 +172,8 @@ mod server {
|
|||||||
.web_proxy_tokens(Arc::new(move |token| signer.is_valid_web_proxy_access_token(&token)))
|
.web_proxy_tokens(Arc::new(move |token| signer.is_valid_web_proxy_access_token(&token)))
|
||||||
.extra_dapps(&extra_dapps)
|
.extra_dapps(&extra_dapps)
|
||||||
.signer_address(deps.signer.address())
|
.signer_address(deps.signer.address())
|
||||||
.allowed_hosts(allowed_hosts);
|
.allowed_hosts(allowed_hosts)
|
||||||
|
.extra_cors_headers(cors);
|
||||||
|
|
||||||
let api_set = if all_apis {
|
let api_set = if all_apis {
|
||||||
warn!("{}", Colour::Red.bold().paint("*** INSECURE *** Running Dapps with all APIs exposed."));
|
warn!("{}", Colour::Red.bold().paint("*** INSECURE *** Running Dapps with all APIs exposed."));
|
||||||
|
Loading…
Reference in New Issue
Block a user