Allow CORS requests in Secret Store API (#10584)
* allow CORS requests for Secret Store API (#10582) * secretstore CORS: fix error with unit tests * secretstore CORS: removed debug log * secretstore CORS: add missing response's header * secretstore CORS: switched to jsonrpc-server-utils for CORS validation
This commit is contained in:
parent
c5fa7aab43
commit
4cc274e75f
33
Cargo.lock
generated
33
Cargo.lock
generated
@ -1074,6 +1074,7 @@ dependencies = [
|
|||||||
"ethkey 0.3.0",
|
"ethkey 0.3.0",
|
||||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"jsonrpc-server-utils 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1853,6 +1854,18 @@ dependencies = [
|
|||||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonrpc-core"
|
||||||
|
version = "11.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-derive"
|
name = "jsonrpc-derive"
|
||||||
version = "10.0.2"
|
version = "10.0.2"
|
||||||
@ -1916,6 +1929,22 @@ dependencies = [
|
|||||||
"unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonrpc-server-utils"
|
||||||
|
version = "11.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"jsonrpc-core 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-tcp-server"
|
name = "jsonrpc-tcp-server"
|
||||||
version = "10.0.1"
|
version = "10.0.1"
|
||||||
@ -2349,7 +2378,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4593,11 +4622,13 @@ dependencies = [
|
|||||||
"checksum jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "294eca097d1dc0bf59de5ab9f7eafa5f77129e9f6464c957ed3ddeb705fb4292"
|
"checksum jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "294eca097d1dc0bf59de5ab9f7eafa5f77129e9f6464c957ed3ddeb705fb4292"
|
||||||
"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||||
"checksum jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a5152c3fda235dfd68341b3edf4121bc4428642c93acbd6de88c26bf95fc5d7"
|
"checksum jsonrpc-core 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a5152c3fda235dfd68341b3edf4121bc4428642c93acbd6de88c26bf95fc5d7"
|
||||||
|
"checksum jsonrpc-core 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b83fdc5e0218128d0d270f2f2e7a5ea716f3240c8518a58bc89e6716ba8581"
|
||||||
"checksum jsonrpc-derive 10.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c14be84e86c75935be83a34c6765bf31f97ed6c9163bb0b83007190e9703940a"
|
"checksum jsonrpc-derive 10.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c14be84e86c75935be83a34c6765bf31f97ed6c9163bb0b83007190e9703940a"
|
||||||
"checksum jsonrpc-http-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "99e1ce36c7cc9dcab398024d76849ab2cb917ee812653bce6f74fc9eb7c82d16"
|
"checksum jsonrpc-http-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "99e1ce36c7cc9dcab398024d76849ab2cb917ee812653bce6f74fc9eb7c82d16"
|
||||||
"checksum jsonrpc-ipc-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fac6b8682243740a32bfb288880c71cc06eca29616cdf551e4136a190b11b96d"
|
"checksum jsonrpc-ipc-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fac6b8682243740a32bfb288880c71cc06eca29616cdf551e4136a190b11b96d"
|
||||||
"checksum jsonrpc-pubsub 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56608ed54b1b2a69f4357cb8bdfbcbd99fe1179383c03a09bb428931bd35f592"
|
"checksum jsonrpc-pubsub 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56608ed54b1b2a69f4357cb8bdfbcbd99fe1179383c03a09bb428931bd35f592"
|
||||||
"checksum jsonrpc-server-utils 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5521613b31ea22d36d9f95ad642058dccec846a94ed8690957652d479f620707"
|
"checksum jsonrpc-server-utils 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5521613b31ea22d36d9f95ad642058dccec846a94ed8690957652d479f620707"
|
||||||
|
"checksum jsonrpc-server-utils 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3372b3248a53abcca8f61924f188052bb0c4cd80b482b2b4eaf9f8667efb9f4"
|
||||||
"checksum jsonrpc-tcp-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c873dac37a601fb88d40ba49eeac3f1aa60953c06b2e99ddbf0569b6f8028478"
|
"checksum jsonrpc-tcp-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c873dac37a601fb88d40ba49eeac3f1aa60953c06b2e99ddbf0569b6f8028478"
|
||||||
"checksum jsonrpc-ws-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20b8333a5a6e6ccbcf5c90f90919de557cba4929efa164e9bd0e8e497eb20e46"
|
"checksum jsonrpc-ws-server 10.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20b8333a5a6e6ccbcf5c90f90919de557cba4929efa164e9bd0e8e497eb20e46"
|
||||||
"checksum keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "253bbe643c32c816bf58fa5a88248fafedeebb139705ad17a62add3517854a86"
|
"checksum keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "253bbe643c32c816bf58fa5a88248fafedeebb139705ad17a62add3517854a86"
|
||||||
|
@ -623,6 +623,10 @@ usage! {
|
|||||||
"--no-secretstore-auto-migrate",
|
"--no-secretstore-auto-migrate",
|
||||||
"Do not run servers set change session automatically when servers set changes. This option has no effect when servers set is read from configuration file.",
|
"Do not run servers set change session automatically when servers set changes. This option has no effect when servers set is read from configuration file.",
|
||||||
|
|
||||||
|
ARG arg_secretstore_http_cors: (String) = "none", or |c: &Config| c.secretstore.as_ref()?.cors.as_ref().map(|vec| vec.join(",")),
|
||||||
|
"--secretstore-http-cors=[URL]",
|
||||||
|
"Specify CORS header for Secret Store HTTP API responses. Special options: \"all\", \"none\".",
|
||||||
|
|
||||||
ARG arg_secretstore_acl_contract: (Option<String>) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.acl_contract.clone(),
|
ARG arg_secretstore_acl_contract: (Option<String>) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.acl_contract.clone(),
|
||||||
"--secretstore-acl-contract=[SOURCE]",
|
"--secretstore-acl-contract=[SOURCE]",
|
||||||
"Secret Store permissioning contract address source: none, registry (contract address is read from 'secretstore_acl_checker' entry in registry) or address.",
|
"Secret Store permissioning contract address source: none, registry (contract address is read from 'secretstore_acl_checker' entry in registry) or address.",
|
||||||
@ -1328,6 +1332,7 @@ struct SecretStore {
|
|||||||
http_interface: Option<String>,
|
http_interface: Option<String>,
|
||||||
http_port: Option<u16>,
|
http_port: Option<u16>,
|
||||||
path: Option<String>,
|
path: Option<String>,
|
||||||
|
cors: Option<Vec<String>>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Deserialize)]
|
#[derive(Default, Debug, PartialEq, Deserialize)]
|
||||||
@ -1854,6 +1859,7 @@ mod tests {
|
|||||||
arg_secretstore_http_interface: "local".into(),
|
arg_secretstore_http_interface: "local".into(),
|
||||||
arg_secretstore_http_port: 8082u16,
|
arg_secretstore_http_port: 8082u16,
|
||||||
arg_secretstore_path: "$HOME/.parity/secretstore".into(),
|
arg_secretstore_path: "$HOME/.parity/secretstore".into(),
|
||||||
|
arg_secretstore_http_cors: "null".into(),
|
||||||
|
|
||||||
// IPFS
|
// IPFS
|
||||||
flag_ipfs_api: false,
|
flag_ipfs_api: false,
|
||||||
@ -2132,6 +2138,7 @@ mod tests {
|
|||||||
http_interface: None,
|
http_interface: None,
|
||||||
http_port: Some(8082),
|
http_port: Some(8082),
|
||||||
path: None,
|
path: None,
|
||||||
|
cors: None,
|
||||||
}),
|
}),
|
||||||
private_tx: None,
|
private_tx: None,
|
||||||
ipfs: Some(Ipfs {
|
ipfs: Some(Ipfs {
|
||||||
|
@ -105,6 +105,7 @@ http_port = 8082
|
|||||||
interface = "local"
|
interface = "local"
|
||||||
port = 8083
|
port = 8083
|
||||||
path = "$HOME/.parity/secretstore"
|
path = "$HOME/.parity/secretstore"
|
||||||
|
cors = ["null"]
|
||||||
|
|
||||||
[ipfs]
|
[ipfs]
|
||||||
enable = false
|
enable = false
|
||||||
|
@ -638,6 +638,7 @@ impl Configuration {
|
|||||||
http_port: self.args.arg_ports_shift + self.args.arg_secretstore_http_port,
|
http_port: self.args.arg_ports_shift + self.args.arg_secretstore_http_port,
|
||||||
data_path: self.directories().secretstore,
|
data_path: self.directories().secretstore,
|
||||||
admin_public: self.secretstore_admin_public()?,
|
admin_public: self.secretstore_admin_public()?,
|
||||||
|
cors: self.secretstore_cors()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1058,6 +1059,10 @@ impl Configuration {
|
|||||||
self.interface(&self.args.arg_secretstore_http_interface)
|
self.interface(&self.args.arg_secretstore_http_interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn secretstore_cors(&self) -> Option<Vec<String>> {
|
||||||
|
Self::cors(self.args.arg_secretstore_http_cors.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
fn secretstore_self_secret(&self) -> Result<Option<NodeSecretKey>, String> {
|
fn secretstore_self_secret(&self) -> Result<Option<NodeSecretKey>, String> {
|
||||||
match self.args.arg_secretstore_secret {
|
match self.args.arg_secretstore_secret {
|
||||||
Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse()
|
Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse()
|
||||||
@ -1969,4 +1974,19 @@ mod tests {
|
|||||||
_ => panic!("Should be Cmd::Run"),
|
_ => panic!("Should be Cmd::Run"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_secretstore_cors() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// when
|
||||||
|
let conf0 = parse(&["parity"]);
|
||||||
|
let conf1 = parse(&["parity", "--secretstore-http-cors", "*"]);
|
||||||
|
let conf2 = parse(&["parity", "--secretstore-http-cors", "http://parity.io,http://something.io"]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(conf0.secretstore_cors(), Some(vec![]));
|
||||||
|
assert_eq!(conf1.secretstore_cors(), None);
|
||||||
|
assert_eq!(conf2.secretstore_cors(), Some(vec!["http://parity.io".into(),"http://something.io".into()]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,8 @@ pub struct Configuration {
|
|||||||
pub data_path: String,
|
pub data_path: String,
|
||||||
/// Administrator public key.
|
/// Administrator public key.
|
||||||
pub admin_public: Option<Public>,
|
pub admin_public: Option<Public>,
|
||||||
|
// Allowed CORS domains
|
||||||
|
pub cors: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Secret store dependencies
|
/// Secret store dependencies
|
||||||
@ -195,6 +197,7 @@ mod server {
|
|||||||
admin_public: conf.admin_public,
|
admin_public: conf.admin_public,
|
||||||
auto_migrate_enabled: conf.auto_migrate_enabled,
|
auto_migrate_enabled: conf.auto_migrate_enabled,
|
||||||
},
|
},
|
||||||
|
cors: conf.cors
|
||||||
};
|
};
|
||||||
|
|
||||||
cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone());
|
cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone());
|
||||||
@ -234,6 +237,7 @@ impl Default for Configuration {
|
|||||||
http_interface: "127.0.0.1".to_owned(),
|
http_interface: "127.0.0.1".to_owned(),
|
||||||
http_port: 8082,
|
http_port: 8082,
|
||||||
data_path: replace_home(&data_dir, "$BASE/secretstore"),
|
data_path: replace_home(&data_dir, "$BASE/secretstore"),
|
||||||
|
cors: Some(vec![]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ tokio = "~0.1.11"
|
|||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
tokio-service = "0.1"
|
tokio-service = "0.1"
|
||||||
url = "1.0"
|
url = "1.0"
|
||||||
|
jsonrpc-server-utils = "11.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.5"
|
env_logger = "0.5"
|
||||||
|
@ -37,6 +37,7 @@ extern crate tokio;
|
|||||||
extern crate tokio_io;
|
extern crate tokio_io;
|
||||||
extern crate tokio_service;
|
extern crate tokio_service;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
extern crate jsonrpc_server_utils;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate ethabi_derive;
|
extern crate ethabi_derive;
|
||||||
@ -107,7 +108,7 @@ pub fn start(client: Arc<Client>, sync: Arc<SyncProvider>, miner: Arc<Miner>, se
|
|||||||
|
|
||||||
// prepare HTTP listener
|
// prepare HTTP listener
|
||||||
let http_listener = match config.listener_address {
|
let http_listener = match config.listener_address {
|
||||||
Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, Arc::downgrade(&key_server), executor)?),
|
Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, config.cors, Arc::downgrade(&key_server), executor)?),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ use traits::KeyServer;
|
|||||||
use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic};
|
use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic};
|
||||||
use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId,
|
use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId,
|
||||||
EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId};
|
EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId};
|
||||||
|
use jsonrpc_server_utils::cors::{self, AllowCors, AccessControlAllowOrigin};
|
||||||
|
|
||||||
/// Key server http-requests listener. Available requests:
|
/// Key server http-requests listener. Available requests:
|
||||||
/// To generate server key: POST /shadow/{server_key_id}/{signature}/{threshold}
|
/// To generate server key: POST /shadow/{server_key_id}/{signature}/{threshold}
|
||||||
@ -45,6 +46,8 @@ use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKey
|
|||||||
/// To generate ECDSA signature with server key: GET /ecdsa/{server_key_id}/{signature}/{message_hash}
|
/// To generate ECDSA signature with server key: GET /ecdsa/{server_key_id}/{signature}/{message_hash}
|
||||||
/// To change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids
|
/// To change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids
|
||||||
|
|
||||||
|
type CorsDomains = Option<Vec<AccessControlAllowOrigin>>;
|
||||||
|
|
||||||
pub struct KeyServerHttpListener {
|
pub struct KeyServerHttpListener {
|
||||||
_executor: Executor,
|
_executor: Executor,
|
||||||
_handler: Arc<KeyServerSharedHttpHandler>,
|
_handler: Arc<KeyServerSharedHttpHandler>,
|
||||||
@ -77,6 +80,7 @@ enum Request {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct KeyServerHttpHandler {
|
struct KeyServerHttpHandler {
|
||||||
handler: Arc<KeyServerSharedHttpHandler>,
|
handler: Arc<KeyServerSharedHttpHandler>,
|
||||||
|
cors: CorsDomains,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shared http handler
|
/// Shared http handler
|
||||||
@ -84,13 +88,14 @@ struct KeyServerSharedHttpHandler {
|
|||||||
key_server: Weak<KeyServer>,
|
key_server: Weak<KeyServer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl KeyServerHttpListener {
|
impl KeyServerHttpListener {
|
||||||
/// Start KeyServer http listener
|
/// Start KeyServer http listener
|
||||||
pub fn start(listener_address: NodeAddress, key_server: Weak<KeyServer>, executor: Executor) -> Result<Self, Error> {
|
pub fn start(listener_address: NodeAddress, cors_domains: Option<Vec<String>>, key_server: Weak<KeyServer>, executor: Executor) -> Result<Self, Error> {
|
||||||
let shared_handler = Arc::new(KeyServerSharedHttpHandler {
|
let shared_handler = Arc::new(KeyServerSharedHttpHandler {
|
||||||
key_server: key_server,
|
key_server: key_server,
|
||||||
});
|
});
|
||||||
|
let cors: CorsDomains = cors_domains.map(|domains| domains.into_iter().map(AccessControlAllowOrigin::from).collect());
|
||||||
let listener_address = format!("{}:{}", listener_address.address, listener_address.port).parse()?;
|
let listener_address = format!("{}:{}", listener_address.address, listener_address.port).parse()?;
|
||||||
let listener = TcpListener::bind(&listener_address)?;
|
let listener = TcpListener::bind(&listener_address)?;
|
||||||
|
|
||||||
@ -101,7 +106,7 @@ impl KeyServerHttpListener {
|
|||||||
.for_each(move |socket| {
|
.for_each(move |socket| {
|
||||||
let http = Http::new();
|
let http = Http::new();
|
||||||
let serve = http.serve_connection(socket,
|
let serve = http.serve_connection(socket,
|
||||||
KeyServerHttpHandler { handler: shared_handler2.clone() }
|
KeyServerHttpHandler { handler: shared_handler2.clone(), cors: cors.clone() }
|
||||||
).map(|_| ()).map_err(|e| {
|
).map(|_| ()).map_err(|e| {
|
||||||
warn!("Key server handler error: {:?}", e);
|
warn!("Key server handler error: {:?}", e);
|
||||||
});
|
});
|
||||||
@ -121,10 +126,10 @@ impl KeyServerHttpListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl KeyServerHttpHandler {
|
impl KeyServerHttpHandler {
|
||||||
fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8]) -> HttpResponse<Body> {
|
fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8], cors: AllowCors<AccessControlAllowOrigin>) -> HttpResponse<Body> {
|
||||||
match parse_request(&req_method, &path, &req_body) {
|
match parse_request(&req_method, &path, &req_body) {
|
||||||
Request::GenerateServerKey(document, signature, threshold) => {
|
Request::GenerateServerKey(document, signature, threshold) => {
|
||||||
return_server_public_key(&req_uri, self.handler.key_server.upgrade()
|
return_server_public_key(&req_uri, cors, self.handler.key_server.upgrade()
|
||||||
.map(|key_server| key_server.generate_key(&document, &signature.into(), threshold))
|
.map(|key_server| key_server.generate_key(&document, &signature.into(), threshold))
|
||||||
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -133,7 +138,7 @@ impl KeyServerHttpHandler {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => {
|
Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => {
|
||||||
return_empty(&req_uri, self.handler.key_server.upgrade()
|
return_empty(&req_uri, cors, self.handler.key_server.upgrade()
|
||||||
.map(|key_server| key_server.store_document_key(&document, &signature.into(), common_point, encrypted_document_key))
|
.map(|key_server| key_server.store_document_key(&document, &signature.into(), common_point, encrypted_document_key))
|
||||||
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -142,7 +147,7 @@ impl KeyServerHttpHandler {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Request::GenerateDocumentKey(document, signature, threshold) => {
|
Request::GenerateDocumentKey(document, signature, threshold) => {
|
||||||
return_document_key(&req_uri, self.handler.key_server.upgrade()
|
return_document_key(&req_uri, cors, self.handler.key_server.upgrade()
|
||||||
.map(|key_server| key_server.generate_document_key(&document, &signature.into(), threshold))
|
.map(|key_server| key_server.generate_document_key(&document, &signature.into(), threshold))
|
||||||
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -151,7 +156,7 @@ impl KeyServerHttpHandler {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Request::GetDocumentKey(document, signature) => {
|
Request::GetDocumentKey(document, signature) => {
|
||||||
return_document_key(&req_uri, self.handler.key_server.upgrade()
|
return_document_key(&req_uri, cors, self.handler.key_server.upgrade()
|
||||||
.map(|key_server| key_server.restore_document_key(&document, &signature.into()))
|
.map(|key_server| key_server.restore_document_key(&document, &signature.into()))
|
||||||
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -160,7 +165,7 @@ impl KeyServerHttpHandler {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Request::GetDocumentKeyShadow(document, signature) => {
|
Request::GetDocumentKeyShadow(document, signature) => {
|
||||||
return_document_key_shadow(&req_uri, self.handler.key_server.upgrade()
|
return_document_key_shadow(&req_uri, cors, self.handler.key_server.upgrade()
|
||||||
.map(|key_server| key_server.restore_document_key_shadow(&document, &signature.into()))
|
.map(|key_server| key_server.restore_document_key_shadow(&document, &signature.into()))
|
||||||
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -169,7 +174,7 @@ impl KeyServerHttpHandler {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Request::SchnorrSignMessage(document, signature, message_hash) => {
|
Request::SchnorrSignMessage(document, signature, message_hash) => {
|
||||||
return_message_signature(&req_uri, self.handler.key_server.upgrade()
|
return_message_signature(&req_uri, cors, self.handler.key_server.upgrade()
|
||||||
.map(|key_server| key_server.sign_message_schnorr(&document, &signature.into(), message_hash))
|
.map(|key_server| key_server.sign_message_schnorr(&document, &signature.into(), message_hash))
|
||||||
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -178,7 +183,7 @@ impl KeyServerHttpHandler {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Request::EcdsaSignMessage(document, signature, message_hash) => {
|
Request::EcdsaSignMessage(document, signature, message_hash) => {
|
||||||
return_message_signature(&req_uri, self.handler.key_server.upgrade()
|
return_message_signature(&req_uri, cors, self.handler.key_server.upgrade()
|
||||||
.map(|key_server| key_server.sign_message_ecdsa(&document, &signature.into(), message_hash))
|
.map(|key_server| key_server.sign_message_ecdsa(&document, &signature.into(), message_hash))
|
||||||
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -187,7 +192,7 @@ impl KeyServerHttpHandler {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => {
|
Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => {
|
||||||
return_empty(&req_uri, self.handler.key_server.upgrade()
|
return_empty(&req_uri, cors, self.handler.key_server.upgrade()
|
||||||
.map(|key_server| key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set))
|
.map(|key_server| key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set))
|
||||||
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
.unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into())))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
@ -213,14 +218,21 @@ impl Service for KeyServerHttpHandler {
|
|||||||
type Future = Box<Future<Item = HttpResponse<Self::ResBody>, Error=Self::Error> + Send>;
|
type Future = Box<Future<Item = HttpResponse<Self::ResBody>, Error=Self::Error> + Send>;
|
||||||
|
|
||||||
fn call(&mut self, req: HttpRequest<Body>) -> Self::Future {
|
fn call(&mut self, req: HttpRequest<Body>) -> Self::Future {
|
||||||
if req.headers().contains_key(header::ORIGIN) {
|
let cors = cors::get_cors_allow_origin(
|
||||||
warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method(), req.uri());
|
req.headers().get(header::ORIGIN).and_then(|value| value.to_str().ok()),
|
||||||
return Box::new(future::ok(HttpResponse::builder()
|
req.headers().get(header::HOST).and_then(|value| value.to_str().ok()),
|
||||||
|
&self.cors
|
||||||
|
);
|
||||||
|
match cors {
|
||||||
|
AllowCors::Invalid => {
|
||||||
|
warn!(target: "secretstore", "Ignoring {}-request {} with unauthorized Origin header", req.method(), req.uri());
|
||||||
|
Box::new(future::ok(HttpResponse::builder()
|
||||||
.status(HttpStatusCode::NOT_FOUND)
|
.status(HttpStatusCode::NOT_FOUND)
|
||||||
.body(Body::empty())
|
.body(Body::empty())
|
||||||
.expect("Nothing to parse, cannot fail; qed")))
|
.expect("Nothing to parse, cannot fail; qed")
|
||||||
}
|
))
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
let req_method = req.method().clone();
|
let req_method = req.method().clone();
|
||||||
let req_uri = req.uri().clone();
|
let req_uri = req.uri().clone();
|
||||||
// We cannot consume Self because of the Service trait requirement.
|
// We cannot consume Self because of the Service trait requirement.
|
||||||
@ -229,7 +241,7 @@ impl Service for KeyServerHttpHandler {
|
|||||||
Box::new(req.into_body().concat2().map(move |body| {
|
Box::new(req.into_body().concat2().map(move |body| {
|
||||||
let path = req_uri.path().to_string();
|
let path = req_uri.path().to_string();
|
||||||
if path.starts_with("/") {
|
if path.starts_with("/") {
|
||||||
this.process(req_method, req_uri, &path, &body)
|
this.process(req_method, req_uri, &path, &body, cors)
|
||||||
} else {
|
} else {
|
||||||
warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri);
|
warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri);
|
||||||
HttpResponse::builder()
|
HttpResponse::builder()
|
||||||
@ -240,42 +252,46 @@ impl Service for KeyServerHttpHandler {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn return_empty(req_uri: &Uri, empty: Result<(), Error>) -> HttpResponse<Body> {
|
|
||||||
return_bytes::<i32>(req_uri, empty.map(|_| None))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_server_public_key(req_uri: &Uri, server_public: Result<Public, Error>) -> HttpResponse<Body> {
|
fn return_empty(req_uri: &Uri, cors: AllowCors<AccessControlAllowOrigin>, empty: Result<(), Error>) -> HttpResponse<Body> {
|
||||||
return_bytes(req_uri, server_public.map(|k| Some(SerializablePublic(k))))
|
return_bytes::<i32>(req_uri, cors, empty.map(|_| None))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_message_signature(req_uri: &Uri, signature: Result<EncryptedDocumentKey, Error>) -> HttpResponse<Body> {
|
fn return_server_public_key(req_uri: &Uri, cors: AllowCors<AccessControlAllowOrigin>, server_public: Result<Public, Error>) -> HttpResponse<Body> {
|
||||||
return_bytes(req_uri, signature.map(|s| Some(SerializableBytes(s))))
|
return_bytes(req_uri, cors, server_public.map(|k| Some(SerializablePublic(k))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_document_key(req_uri: &Uri, document_key: Result<EncryptedDocumentKey, Error>) -> HttpResponse<Body> {
|
fn return_message_signature(req_uri: &Uri, cors: AllowCors<AccessControlAllowOrigin>, signature: Result<EncryptedDocumentKey, Error>) -> HttpResponse<Body> {
|
||||||
return_bytes(req_uri, document_key.map(|k| Some(SerializableBytes(k))))
|
return_bytes(req_uri, cors, signature.map(|s| Some(SerializableBytes(s))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_document_key_shadow(req_uri: &Uri, document_key_shadow: Result<EncryptedDocumentKeyShadow, Error>)
|
fn return_document_key(req_uri: &Uri, cors: AllowCors<AccessControlAllowOrigin>, document_key: Result<EncryptedDocumentKey, Error>) -> HttpResponse<Body> {
|
||||||
|
return_bytes(req_uri, cors, document_key.map(|k| Some(SerializableBytes(k))))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_document_key_shadow(req_uri: &Uri, cors: AllowCors<AccessControlAllowOrigin>, document_key_shadow: Result<EncryptedDocumentKeyShadow, Error>)
|
||||||
-> HttpResponse<Body>
|
-> HttpResponse<Body>
|
||||||
{
|
{
|
||||||
return_bytes(req_uri, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow {
|
return_bytes(req_uri, cors, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow {
|
||||||
decrypted_secret: k.decrypted_secret.into(),
|
decrypted_secret: k.decrypted_secret.into(),
|
||||||
common_point: k.common_point.expect("always filled when requesting document_key_shadow; qed").into(),
|
common_point: k.common_point.expect("always filled when requesting document_key_shadow; qed").into(),
|
||||||
decrypt_shadows: k.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect(),
|
decrypt_shadows: k.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect()
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_bytes<T: Serialize>(req_uri: &Uri, result: Result<Option<T>, Error>) -> HttpResponse<Body> {
|
fn return_bytes<T: Serialize>(req_uri: &Uri, cors: AllowCors<AccessControlAllowOrigin>, result: Result<Option<T>, Error>) -> HttpResponse<Body> {
|
||||||
match result {
|
match result {
|
||||||
Ok(Some(result)) => match serde_json::to_vec(&result) {
|
Ok(Some(result)) => match serde_json::to_vec(&result) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
let body: Body = result.into();
|
let body: Body = result.into();
|
||||||
HttpResponse::builder()
|
let mut builder = HttpResponse::builder();
|
||||||
.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8"))
|
builder.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8"));
|
||||||
.body(body)
|
if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors {
|
||||||
.expect("Error creating http response")
|
builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string());
|
||||||
|
}
|
||||||
|
builder.body(body).expect("Error creating http response")
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err);
|
warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err);
|
||||||
@ -286,10 +302,12 @@ fn return_bytes<T: Serialize>(req_uri: &Uri, result: Result<Option<T>, Error>) -
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
HttpResponse::builder()
|
let mut builder = HttpResponse::builder();
|
||||||
.status(HttpStatusCode::OK)
|
builder.status(HttpStatusCode::OK);
|
||||||
.body(Body::empty())
|
if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors {
|
||||||
.expect("Nothing to parse, cannot fail; qed")
|
builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string());
|
||||||
|
}
|
||||||
|
builder.body(Body::empty()).expect("Nothing to parse, cannot fail; qed")
|
||||||
},
|
},
|
||||||
Err(err) => return_error(err),
|
Err(err) => return_error(err),
|
||||||
}
|
}
|
||||||
@ -423,7 +441,7 @@ mod tests {
|
|||||||
let key_server: Arc<KeyServer> = Arc::new(DummyKeyServer::default());
|
let key_server: Arc<KeyServer> = Arc::new(DummyKeyServer::default());
|
||||||
let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 };
|
let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 };
|
||||||
let runtime = Runtime::with_thread_count(1);
|
let runtime = Runtime::with_thread_count(1);
|
||||||
let listener = KeyServerHttpListener::start(address, Arc::downgrade(&key_server),
|
let listener = KeyServerHttpListener::start(address, None, Arc::downgrade(&key_server),
|
||||||
runtime.executor()).unwrap();
|
runtime.executor()).unwrap();
|
||||||
drop(listener);
|
drop(listener);
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,8 @@ pub struct ServiceConfiguration {
|
|||||||
pub acl_check_contract_address: Option<ContractAddress>,
|
pub acl_check_contract_address: Option<ContractAddress>,
|
||||||
/// Cluster configuration.
|
/// Cluster configuration.
|
||||||
pub cluster_config: ClusterConfiguration,
|
pub cluster_config: ClusterConfiguration,
|
||||||
|
// Allowed CORS domains
|
||||||
|
pub cors: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Key server cluster configuration
|
/// Key server cluster configuration
|
||||||
|
Loading…
Reference in New Issue
Block a user