From 4cc274e75f2c4e86bbe7fd42bcee816f08c926de Mon Sep 17 00:00:00 2001 From: Antoine Detante Date: Sat, 20 Apr 2019 07:31:37 +0200 Subject: [PATCH] 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 --- Cargo.lock | 33 +++++- parity/cli/mod.rs | 7 ++ parity/cli/tests/config.full.toml | 1 + parity/configuration.rs | 20 ++++ parity/secretstore.rs | 4 + secret-store/Cargo.toml | 1 + secret-store/src/lib.rs | 3 +- secret-store/src/listener/http_listener.rs | 130 ++++++++++++--------- secret-store/src/types/all.rs | 2 + 9 files changed, 143 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c7c31f56..42e2ed854 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1074,6 +1074,7 @@ dependencies = [ "ethkey 0.3.0", "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)", + "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)", "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)", @@ -1853,6 +1854,18 @@ dependencies = [ "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]] name = "jsonrpc-derive" version = "10.0.2" @@ -1916,6 +1929,22 @@ dependencies = [ "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]] name = "jsonrpc-tcp-server" version = "10.0.1" @@ -2349,7 +2378,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "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)", - "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)", ] @@ -4593,11 +4622,13 @@ dependencies = [ "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 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-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-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 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-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" diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index a2918f85e..4e863dd85 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -623,6 +623,10 @@ usage! { "--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.", + 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) = Some("registry".into()), or |c: &Config| c.secretstore.as_ref()?.acl_contract.clone(), "--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.", @@ -1328,6 +1332,7 @@ struct SecretStore { http_interface: Option, http_port: Option, path: Option, + cors: Option> } #[derive(Default, Debug, PartialEq, Deserialize)] @@ -1854,6 +1859,7 @@ mod tests { arg_secretstore_http_interface: "local".into(), arg_secretstore_http_port: 8082u16, arg_secretstore_path: "$HOME/.parity/secretstore".into(), + arg_secretstore_http_cors: "null".into(), // IPFS flag_ipfs_api: false, @@ -2132,6 +2138,7 @@ mod tests { http_interface: None, http_port: Some(8082), path: None, + cors: None, }), private_tx: None, ipfs: Some(Ipfs { diff --git a/parity/cli/tests/config.full.toml b/parity/cli/tests/config.full.toml index 34dd39058..3491658c5 100644 --- a/parity/cli/tests/config.full.toml +++ b/parity/cli/tests/config.full.toml @@ -105,6 +105,7 @@ http_port = 8082 interface = "local" port = 8083 path = "$HOME/.parity/secretstore" +cors = ["null"] [ipfs] enable = false diff --git a/parity/configuration.rs b/parity/configuration.rs index 198a58c3f..10b0f3904 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -638,6 +638,7 @@ impl Configuration { http_port: self.args.arg_ports_shift + self.args.arg_secretstore_http_port, data_path: self.directories().secretstore, admin_public: self.secretstore_admin_public()?, + cors: self.secretstore_cors() }) } @@ -1058,6 +1059,10 @@ impl Configuration { self.interface(&self.args.arg_secretstore_http_interface) } + fn secretstore_cors(&self) -> Option> { + Self::cors(self.args.arg_secretstore_http_cors.as_ref()) + } + fn secretstore_self_secret(&self) -> Result, String> { match self.args.arg_secretstore_secret { Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse() @@ -1969,4 +1974,19 @@ mod tests { _ => 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()])); + } } diff --git a/parity/secretstore.rs b/parity/secretstore.rs index d9075edec..bde67e344 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -84,6 +84,8 @@ pub struct Configuration { pub data_path: String, /// Administrator public key. pub admin_public: Option, + // Allowed CORS domains + pub cors: Option>, } /// Secret store dependencies @@ -195,6 +197,7 @@ mod server { admin_public: conf.admin_public, 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()); @@ -234,6 +237,7 @@ impl Default for Configuration { http_interface: "127.0.0.1".to_owned(), http_port: 8082, data_path: replace_home(&data_dir, "$BASE/secretstore"), + cors: Some(vec![]), } } } diff --git a/secret-store/Cargo.toml b/secret-store/Cargo.toml index 0303472fe..edf329706 100644 --- a/secret-store/Cargo.toml +++ b/secret-store/Cargo.toml @@ -36,6 +36,7 @@ tokio = "~0.1.11" tokio-io = "0.1" tokio-service = "0.1" url = "1.0" +jsonrpc-server-utils = "11.0" [dev-dependencies] env_logger = "0.5" diff --git a/secret-store/src/lib.rs b/secret-store/src/lib.rs index a3c82991b..025716704 100644 --- a/secret-store/src/lib.rs +++ b/secret-store/src/lib.rs @@ -37,6 +37,7 @@ extern crate tokio; extern crate tokio_io; extern crate tokio_service; extern crate url; +extern crate jsonrpc_server_utils; #[macro_use] extern crate ethabi_derive; @@ -107,7 +108,7 @@ pub fn start(client: Arc, sync: Arc, miner: Arc, se // prepare HTTP listener 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, }; diff --git a/secret-store/src/listener/http_listener.rs b/secret-store/src/listener/http_listener.rs index aa67ec5c6..d2c3e3d66 100644 --- a/secret-store/src/listener/http_listener.rs +++ b/secret-store/src/listener/http_listener.rs @@ -34,6 +34,7 @@ use traits::KeyServer; use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic}; use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; +use jsonrpc_server_utils::cors::{self, AllowCors, AccessControlAllowOrigin}; /// Key server http-requests listener. Available requests: /// 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 change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids +type CorsDomains = Option>; + pub struct KeyServerHttpListener { _executor: Executor, _handler: Arc, @@ -77,6 +80,7 @@ enum Request { #[derive(Clone)] struct KeyServerHttpHandler { handler: Arc, + cors: CorsDomains, } /// Shared http handler @@ -84,13 +88,14 @@ struct KeyServerSharedHttpHandler { key_server: Weak, } + impl KeyServerHttpListener { /// Start KeyServer http listener - pub fn start(listener_address: NodeAddress, key_server: Weak, executor: Executor) -> Result { + pub fn start(listener_address: NodeAddress, cors_domains: Option>, key_server: Weak, executor: Executor) -> Result { let shared_handler = Arc::new(KeyServerSharedHttpHandler { 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 = TcpListener::bind(&listener_address)?; @@ -101,7 +106,7 @@ impl KeyServerHttpListener { .for_each(move |socket| { let http = Http::new(); let serve = http.serve_connection(socket, - KeyServerHttpHandler { handler: shared_handler2.clone() } + KeyServerHttpHandler { handler: shared_handler2.clone(), cors: cors.clone() } ).map(|_| ()).map_err(|e| { warn!("Key server handler error: {:?}", e); }); @@ -121,10 +126,10 @@ impl KeyServerHttpListener { } impl KeyServerHttpHandler { - fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8]) -> HttpResponse { + fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8], cors: AllowCors) -> HttpResponse { match parse_request(&req_method, &path, &req_body) { 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)) .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) .map_err(|err| { @@ -133,7 +138,7 @@ impl KeyServerHttpHandler { })) }, 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)) .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) .map_err(|err| { @@ -142,7 +147,7 @@ impl KeyServerHttpHandler { })) }, 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)) .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) .map_err(|err| { @@ -151,7 +156,7 @@ impl KeyServerHttpHandler { })) }, 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())) .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) .map_err(|err| { @@ -160,7 +165,7 @@ impl KeyServerHttpHandler { })) }, 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())) .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) .map_err(|err| { @@ -169,7 +174,7 @@ impl KeyServerHttpHandler { })) }, 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)) .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) .map_err(|err| { @@ -178,7 +183,7 @@ impl KeyServerHttpHandler { })) }, 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)) .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) .map_err(|err| { @@ -187,7 +192,7 @@ impl KeyServerHttpHandler { })) }, 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)) .unwrap_or(Err(Error::Internal("KeyServer is already destroyed".into()))) .map_err(|err| { @@ -213,69 +218,80 @@ impl Service for KeyServerHttpHandler { type Future = Box, Error=Self::Error> + Send>; fn call(&mut self, req: HttpRequest) -> Self::Future { - if req.headers().contains_key(header::ORIGIN) { - warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method(), req.uri()); - return Box::new(future::ok(HttpResponse::builder() - .status(HttpStatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed"))) - } + let cors = cors::get_cors_allow_origin( + req.headers().get(header::ORIGIN).and_then(|value| value.to_str().ok()), + 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) + .body(Body::empty()) + .expect("Nothing to parse, cannot fail; qed") + )) + }, + _ => { + let req_method = req.method().clone(); + let req_uri = req.uri().clone(); + // We cannot consume Self because of the Service trait requirement. + let this = self.clone(); - let req_method = req.method().clone(); - let req_uri = req.uri().clone(); - // We cannot consume Self because of the Service trait requirement. - let this = self.clone(); - - Box::new(req.into_body().concat2().map(move |body| { - let path = req_uri.path().to_string(); - if path.starts_with("/") { - this.process(req_method, req_uri, &path, &body) - } else { - warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); - HttpResponse::builder() - .status(HttpStatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") + Box::new(req.into_body().concat2().map(move |body| { + let path = req_uri.path().to_string(); + if path.starts_with("/") { + this.process(req_method, req_uri, &path, &body, cors) + } else { + warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); + HttpResponse::builder() + .status(HttpStatusCode::NOT_FOUND) + .body(Body::empty()) + .expect("Nothing to parse, cannot fail; qed") + } + })) } - })) + } } } -fn return_empty(req_uri: &Uri, empty: Result<(), Error>) -> HttpResponse { - return_bytes::(req_uri, empty.map(|_| None)) +fn return_empty(req_uri: &Uri, cors: AllowCors, empty: Result<(), Error>) -> HttpResponse { + return_bytes::(req_uri, cors, empty.map(|_| None)) } -fn return_server_public_key(req_uri: &Uri, server_public: Result) -> HttpResponse { - return_bytes(req_uri, server_public.map(|k| Some(SerializablePublic(k)))) +fn return_server_public_key(req_uri: &Uri, cors: AllowCors, server_public: Result) -> HttpResponse { + return_bytes(req_uri, cors, server_public.map(|k| Some(SerializablePublic(k)))) } -fn return_message_signature(req_uri: &Uri, signature: Result) -> HttpResponse { - return_bytes(req_uri, signature.map(|s| Some(SerializableBytes(s)))) +fn return_message_signature(req_uri: &Uri, cors: AllowCors, signature: Result) -> HttpResponse { + return_bytes(req_uri, cors, signature.map(|s| Some(SerializableBytes(s)))) } -fn return_document_key(req_uri: &Uri, document_key: Result) -> HttpResponse { - return_bytes(req_uri, document_key.map(|k| Some(SerializableBytes(k)))) +fn return_document_key(req_uri: &Uri, cors: AllowCors, document_key: Result) -> HttpResponse { + return_bytes(req_uri, cors, document_key.map(|k| Some(SerializableBytes(k)))) } -fn return_document_key_shadow(req_uri: &Uri, document_key_shadow: Result) +fn return_document_key_shadow(req_uri: &Uri, cors: AllowCors, document_key_shadow: Result) -> HttpResponse { - 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(), 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(req_uri: &Uri, result: Result, Error>) -> HttpResponse { +fn return_bytes(req_uri: &Uri, cors: AllowCors, result: Result, Error>) -> HttpResponse { match result { Ok(Some(result)) => match serde_json::to_vec(&result) { Ok(result) => { let body: Body = result.into(); - HttpResponse::builder() - .header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8")) - .body(body) - .expect("Error creating http response") + let mut builder = HttpResponse::builder(); + builder.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8")); + if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors { + builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string()); + } + builder.body(body).expect("Error creating http response") }, Err(err) => { warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err); @@ -286,10 +302,12 @@ fn return_bytes(req_uri: &Uri, result: Result, Error>) - } }, Ok(None) => { - HttpResponse::builder() - .status(HttpStatusCode::OK) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") + let mut builder = HttpResponse::builder(); + builder.status(HttpStatusCode::OK); + if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors { + 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), } @@ -423,7 +441,7 @@ mod tests { let key_server: Arc = Arc::new(DummyKeyServer::default()); let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 }; 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(); drop(listener); } diff --git a/secret-store/src/types/all.rs b/secret-store/src/types/all.rs index 3a1e9df70..1cacb7550 100644 --- a/secret-store/src/types/all.rs +++ b/secret-store/src/types/all.rs @@ -70,6 +70,8 @@ pub struct ServiceConfiguration { pub acl_check_contract_address: Option, /// Cluster configuration. pub cluster_config: ClusterConfiguration, + // Allowed CORS domains + pub cors: Option>, } /// Key server cluster configuration